1/* $NetBSD: main.c,v 1.7 2016/06/11 06:29:24 dholland Exp $ */
2
3/*
4 * Copyright (c) 2003 Naoto Shimazaki.
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 NAOTO SHIMAZAKI AND CONTRIBUTORS ``AS IS''
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE NAOTO OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Boot loader for L-Card+
31 *
32 * ROM Map
33 * -------
34 * ROM1
35 * BFFF FFFF	------------------------------
36 *
37 *		reserved
38 *
39 * BF80 0000	------------------------------
40 *
41 * ROM0
42 * BFFF FFFF	------------------------------
43 *
44 *		user storage (max 2Mbytes)
45 *
46 * BFE0 0000	------------------------------
47 *
48 *		reserved
49 *
50 * BFD4 0000	------------------------------
51 *
52 *		boot params
53 *
54 * BFD2 0000	------------------------------
55 *
56 *		second boot loader (mirror image)
57 *		or Linux Kernel
58 *
59 * BFD0 0000	------------------------------
60 *
61 *		first boot loader (L-Card+ original loader)
62 *
63 *		reset vector
64 * BFC0 0000	------------------------------
65 *
66 *		gziped kernel image (max 4Mbytes)
67 *
68 * BF80 0000	------------------------------
69 *
70 *
71 *
72 * RAM Map
73 * -------
74 *
75 * 80FF FFFF	------------------------------
76 *		ROM ICE work
77 * 80FF FE00	------------------------------
78 *		ROM ICE stack
79 * 80FF FDA8	------------------------------
80 *
81 *
82 *
83 *		kernel
84 * 8004 0000	------------------------------
85 *		kernel stack (growing to lower)
86 *
87 *
88 *		boot loader heap (growing to upper)
89 *		boot loader text & data (at exec time)
90 * 8000 1000	------------------------------
91 *		vector table
92 * 8000 0000	------------------------------
93 *
94 *		virtual memory space
95 *
96 * 0000 0000	------------------------------
97 *
98 *
99 *
100 * ROMCS0 <-> ROMCS3 mapping
101 *
102 *  ROMCS0        ROMCS3
103 * BE7F FFFF <-> BFFF FFFF
104 * BE40 0000 <-> BFC0 0000	reset vector
105 * BE00 0000 <-> BF80 0000
106 *
107 *
108 */
109#include <sys/cdefs.h>
110__KERNEL_RCSID(0, "$NetBSD: main.c,v 1.7 2016/06/11 06:29:24 dholland Exp $");
111
112#include <lib/libsa/stand.h>
113
114#include <lib/libsa/loadfile.h>
115#include <lib/libkern/libkern.h>
116
117#include <hpcmips/vr/vripreg.h>
118#include <hpcmips/vr/cmureg.h>
119#include <hpcmips/vr/vr4181giureg.h>
120
121#include "extern.h"
122#include "i28f128reg.h"
123
124/* XXX */
125#define ISABRGCTL	0x00
126#define ISABRGSTS	0x02
127#define XISACTL		0x04
128
129#define BOOTTIMEOUT	9	/* must less than 10 */
130#define LINEBUFLEN	80
131
132extern const char bootprog_rev[];
133extern const char bootprog_name[];
134
135static void command_help(char *opt);
136static void command_dump(char *opt);
137static void command_boot(char *opt);
138static void command_load(char *opt);
139static void command_fill(char *opt);
140static void command_write(char *opt);
141static void command_option(char *subcmd);
142static void opt_subcmd_print(char *opt);
143static void opt_subcmd_read(char *opt);
144static void opt_subcmd_write(char *opt);
145static void opt_subcmd_path(char *opt);
146static void opt_subcmd_bootp(char *opt);
147static void opt_subcmd_ip(char *opt);
148
149
150struct boot_option	bootopts;
151
152static struct bootmenu_command commands[] = {
153	{ "?",		command_help },
154	{ "h",		command_help },
155	{ "d",		command_dump },
156	{ "b",		command_boot },
157	{ "l",		command_load },
158	{ "f",		command_fill },
159	{ "w",		command_write },
160	{ "o",		command_option },
161	{ NULL,		NULL },
162};
163
164static struct bootmenu_command opt_subcommands[] = {
165	{ "p",		opt_subcmd_print },
166	{ "r",		opt_subcmd_read },
167	{ "w",		opt_subcmd_write },
168	{ "path",	opt_subcmd_path },
169	{ "bootp",	opt_subcmd_bootp },
170	{ "ip",		opt_subcmd_ip },
171	{ NULL,		NULL },
172};
173
174static void
175print_banner(void)
176{
177	printf("\n");
178	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
179#if 0
180	printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
181#endif
182}
183
184static void
185init_devices(void)
186{
187	/* Init RTC */
188	REGWRITE_2(VRETIMEH, 0, 0);
189	REGWRITE_2(VRETIMEM, 0, 0);
190	REGWRITE_2(VRETIMEL, 0, 0);
191
192
193	/*
194	 * CLKSPEEDREG	0x6012
195	 *	DIV	DIV2 mode
196	 *	CLKSP	18 (0x12)
197	 *	PClock (CPU clock)		65.536MHz
198	 *		PClock = (18.432MHz / CLKSP) x 64
199	 *		       = (18.432MHz / 18) x 64
200	 *		       = 65.536MHz
201	 *	TClock (peripheral clock)	32.768MHz
202	 *		TClock = PClock / DIV
203	 *		       = 65.536MHz / 2
204	 *		       = 32.768MHz
205	 */
206
207	/*
208	 * setup ISA BUS clock freqency
209	 *
210	 * set PCLK (internal peripheral clock) to 32.768MHz (TClock / 1)
211	 * set External ISA bus clock to 10.922MHz (TClock / 3)
212	 */
213	REGWRITE_2(VR4181_ISABRG_ADDR, ISABRGCTL, 0x0003);
214	REGWRITE_2(VR4181_ISABRG_ADDR, XISACTL, 0x0401);
215
216	/*
217	 * setup peripheral's clock supply
218	 *
219	 * CSU: disable
220	 * AIU: enable (AIU, ADU, ADU18M)
221	 * PIU: disable
222	 * SIU: enable (SIU18M)
223	 */
224	REGWRITE_2(VR4181_CMU_ADDR, 0, CMUMASK_SIU | CMUMASK_AIU);
225
226	/*
227	 * setup GPIO
228	 */
229#if 0
230	/* L-Card+ generic setup */
231	/*
232	 * pin   mode	comment
233	 * GP0 : GPI	not used
234	 * GP1 : GPI	not used
235	 * GP2 : GPO	LED6 (0: on  1: off)
236	 * GP3 : PCS0	chip select for CS8900A Lan controller
237	 * GP4 : GPI	IRQ input from CS8900A
238	 * GP5 : GPI	not used
239	 * GP6 : GPI	not used
240	 * GP7 : GPI	reserved by TANBAC TB0193
241	 */
242	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
243	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
244		   GP3_PCS0 | GP2_GPO);
245	/*
246	 * pin   mode	comment
247	 * GP8 : GPO	LED5 (0: on  1: off)
248	 * GP9 : GPI	CD2
249	 * GP10: GPI	CD1
250	 * GP11: GPI	not used
251	 * GP12: GPI	not used
252	 * GP13: GPI	not used
253	 * GP14: GPI	not used
254	 * GP15: GPI	not used
255	 */
256	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W, GP8_GPO);
257	/*
258	 * pin   mode	comment
259	 * GP16: IORD	ISA bus
260	 * GP17: IOWR	ISA bus
261	 * GP18: IORDY	ISA bus
262	 * GP19: GPI	not used
263	 * GP20: GPI	not used
264	 * GP21: RESET	resets CS8900A
265	 * GP22: ROMCS0	ROM chip select
266	 * GP23: ROMCS1	ROM chip select
267	 */
268	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
269		   GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
270		   | GP18_IORDY | GP17_IOWR | GP16_IORD);
271	/*
272	 * GP24: ROMCS2	ROM chip select
273	 * GP25: RxD1	SIU1
274	 * GP26: TxD1	SIU1
275	 * GP27: RTS1	SIU1
276	 * GP28: CTS1	SIU1
277	 * GP29: GPI	LED3
278	 * GP30: GPI	reserved by TANBAC TB0193
279	 * GP31: GPI	LED4
280	 */
281	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
282		   GP30_GPI
283		   | GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
284		   | GP24_ROMCS2);
285#else
286	/* e-care node specific setup */
287	/*
288	 * pin   mode	comment
289	 * GP0 : GPO	ECNRTC_RST
290	 * GP1 : GPO	ECNRTC_CLK
291	 * GP2 : GPO	LED6 (0: on  1: off)
292	 * GP3 : PCS0	chip select for CS8900A Lan controller
293	 * GP4 : GPI	IRQ input from CS8900A
294	 * GP5 : GPO	ECNRTC_DIR
295	 * GP6 : GPO	ECNRTC_OUT
296	 * GP7 : GPI	reserved by TANBAC TB0193
297	 */
298	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PIOD_L_REG_W, 0xffff);
299	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE0_REG_W,
300		   GP6_GPO | GP5_GPO | GP3_PCS0
301		   | GP2_GPO | GP1_GPO | GP0_GPO);
302
303	/*
304	 * pin   mode	comment
305	 * GP8 : GPO	LED5 (0: on  1: off)
306	 * GP9 : GPI	CD2
307	 * GP10: GPI	CD1
308	 * GP11: GPI	not used
309	 * GP12: GPI	ECNRTC_IN
310	 * GP13: GPI	not used
311	 * GP14: GPI	not used
312	 * GP15: GPI	not used
313	 */
314	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE1_REG_W,
315		   GP12_GPI | GP8_GPO);
316
317	/*
318	 * pin   mode	comment
319	 * GP16: IORD	ISA bus
320	 * GP17: IOWR	ISA bus
321	 * GP18: IORDY	ISA bus
322	 * GP19: GPI	not used
323	 * GP20: GPI	not used
324	 * GP21: RESET	resets CS8900A
325	 * GP22: ROMCS0	ROM chip select
326	 * GP23: ROMCS1	ROM chip select
327	 */
328	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE2_REG_W,
329		   GP23_ROMCS1 | GP22_ROMCS0 | GP21_RESET
330		   | GP18_IORDY | GP17_IOWR | GP16_IORD);
331	/*
332	 * GP24: ROMCS2	ROM chip select
333	 * GP25: RxD1	SIU1
334	 * GP26: TxD1	SIU1
335	 * GP27: RTS1	SIU1
336	 * GP28: CTS1	SIU1
337	 * GP29: GPI	LED3
338	 * GP30: GPI	reserved by TANBAC TB0193
339	 * GP31: GPI	LED4
340	 */
341	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_MODE3_REG_W,
342		   GP30_GPI
343		   | GP28_CTS1 | GP27_RTS1 | GP26_TxD1 | GP25_RxD1
344		   | GP24_ROMCS2);
345#endif
346
347#if 0
348	/*
349	 * setup interrupt
350	 *
351	 * I4TYP:  falling edge trigger
352	 * GIMSK4: unmask
353	 * GIEN4:  enable
354	 * other:  unused, mask, disable
355	 */
356	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTTYP_L_REG_W,
357		   I4TYP_HIGH_LEVEL);
358	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTMASK_REG_W,
359		   0xffffU & ~GIMSK4);
360	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_INTEN_REG_W, GIEN4);
361#endif
362
363	/*
364	 * programmable chip select
365	 *
366	 * PCS0 is used to select CS8900A Ethernet controller
367	 * on TB0193
368	 *
369	 * PCS0:
370	 *	0x14010000 - 0x14010fff
371	 *	I/O access, 16bit cycle, both of read/write
372	 * PCS1: unused
373	 */
374	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STRA_REG_W, 0x0000);
375	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0STPA_REG_W, 0x0fff);
376	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCS0HIA_REG_W, 0x1401);
377	REGWRITE_2(VR4181_GIU81_ADDR, VR4181GIU_PCSMODE_REG_W,
378		   PCS0MIOB_IO | PCS0DSIZE_16BIT | PCS0MD_READWRITE);
379}
380
381/*
382 * chops the head from the arguments and returns the arguments if any,
383 * or possibly an empty string.
384 */
385static char *
386get_next_arg(char *arg)
387{
388	char *opt;
389
390	if ((opt = strchr(arg, ' ')) == NULL) {
391		opt = "";
392	} else {
393		*opt++ = '\0';
394	}
395
396        /* trim leading blanks */
397	while (*opt == ' ')
398		opt++;
399
400	return opt;
401}
402
403static void
404command_help(char *opt)
405{
406	printf("commands are:\n"
407	       "boot:\tb\n"
408	       "dump:\td addr [addr]\n"
409	       "fill:\tf addr addr char\n"
410	       "load:\tl [offset] (with following S-Record)\n"
411	       "write:\tw dst src len\n"
412	       "option:\to subcommand [params]\n"
413	       "help:\th|?\n"
414	       "\n"
415	       "option subcommands are:\n"
416	       "print:\to p\n"
417	       "read:\to r\n"
418	       "write:\to w\n"
419	       "path:\to path pathname\n"
420	       "bootp:\to bootp yes|no\n"
421	       "ip:\to ip remote local netmask gateway\n"
422		);
423}
424
425static void
426bad_param(void)
427{
428	printf("bad param\n");
429	command_help(NULL);
430}
431
432static const u_int8_t print_cnv[] = {
433	'0', '1', '2', '3', '4', '5', '6', '7',
434	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
435
436static void
437printhexul(u_int32_t n)
438{
439	int	i;
440
441	for (i = 28; i >= 0; i -= 4)
442		putchar(print_cnv[(n >> i) & 0x0f]);
443}
444
445static void
446printhexuc(u_int8_t n)
447{
448	int	i;
449
450	for (i = 4; i >= 0; i -= 4)
451		putchar(print_cnv[(n >> i) & 0x0f]);
452}
453
454static void
455command_dump(char *opt)
456{
457	char		*endptr;
458	const char	*p;
459	const char	*line_fence;
460	const char	*limit;
461
462	p = (const char *) strtoul(opt, &endptr, 16);
463	if (opt == endptr) {
464		bad_param();
465		return;
466	}
467
468	opt = get_next_arg(opt);
469	limit = (const char *) strtoul(opt, &endptr, 16);
470	if (opt == endptr) {
471		limit = p + 256;
472	}
473
474	for (;;) {
475		printhexul((u_int32_t) p);
476		putchar(' ');
477		line_fence = p + 16;
478		while (p < line_fence) {
479			printhexuc(*p++);
480			putchar(' ');
481			if (p >= limit) {
482				putchar('\n');
483				return;
484			}
485		}
486		putchar('\n');
487		if (ISKEY) {
488			if (getchar() == '\x03')
489				break;
490		}
491	}
492}
493
494static void
495command_boot(char *opt)
496{
497	u_long	marks[MARK_MAX];
498
499	marks[MARK_START] = 0;
500	if (loadfile(bootopts.b_pathname, marks, LOAD_KERNEL)) {
501		printf("loadfile failed\n");
502		return;
503	}
504	start_netbsd();
505	/* no return */
506}
507
508/*
509 * loading S-Record
510 */
511static int
512load_srec(char *offset)
513{
514	char		s2lbuf[9];
515	char		c;
516	char		rectype;
517	u_int32_t	reclen;
518	u_int32_t	reclen_bk;
519	u_int32_t	recaddr;
520	char		*endptr;
521	char		*p;
522	u_int32_t	sum;
523	int		err = 0;
524
525	for (;;) {
526		/*
527		 * the first step is to read a S-Record.
528		 */
529		if ((c = getchar()) != 'S')
530			goto out;
531
532		rectype = getchar();
533
534		s2lbuf[0] = getchar();
535		s2lbuf[1] = getchar();
536		s2lbuf[2] = '\0';
537		reclen_bk = reclen = strtoul(s2lbuf, &endptr, 16);
538		if (endptr != &s2lbuf[2])
539			goto out;
540		sum = reclen;
541
542		p = s2lbuf;
543
544		switch (rectype) {
545		case '0':
546			/* just ignore */
547			do {
548				c = getchar();
549			} while (c != '\r' && c != '\n');
550			continue;
551
552		case '3':
553			*p++ = getchar();
554			*p++ = getchar();
555			reclen--;
556			/* FALLTHRU */
557		case '2':
558			*p++ = getchar();
559			*p++ = getchar();
560			reclen--;
561			/* FALLTHRU */
562		case '1':
563			*p++ = getchar();
564			*p++ = getchar();
565			*p++ = getchar();
566			*p++ = getchar();
567			*p = '\0';
568			reclen -= 2;
569
570			recaddr = strtoul(s2lbuf, &endptr, 16);
571			if (endptr != p)
572				goto out;
573			sum += (recaddr >> 24) & 0xff;
574			sum += (recaddr >> 16) & 0xff;
575			sum += (recaddr >> 8) & 0xff;
576			sum += recaddr & 0xff;
577
578			p = offset + recaddr;
579			/*
580			 * XXX
581			 * address range is must be chaked here!
582			 */
583			reclen--;
584			s2lbuf[2] = '\0';
585			while (reclen > 0) {
586				s2lbuf[0] = getchar();
587				s2lbuf[1] = getchar();
588				*p = (u_int8_t) strtoul(s2lbuf, &endptr, 16);
589				if (endptr != &s2lbuf[2])
590					goto out;
591				sum += *p++;
592				reclen--;
593			}
594			break;
595
596		case '7':
597		case '8':
598		case '9':
599			goto out2;
600
601		default:
602			goto out;
603		}
604
605		s2lbuf[0] = getchar();
606		s2lbuf[1] = getchar();
607		s2lbuf[2] = '\0';
608		sum += (strtoul(s2lbuf, &endptr, 16) & 0xff);
609		sum &= 0xff;
610		if (sum != 0xff) {
611			printf("checksum error\n");
612			err = 1;
613			goto out2;
614		}
615
616		c = getchar();
617		if (c != '\r' && c != '\n')
618			goto out;
619	}
620	/* never reach */
621	return 1;
622
623out:
624	printf("invalid S-Record\n");
625	err = 1;
626
627out2:
628	do {
629		c = getchar();
630	} while (c != '\r' && c != '\n');
631
632	return err;
633}
634
635static void
636command_load(char *opt)
637{
638	char	*endptr;
639	char	*offset;
640
641	offset = (char *) strtoul(opt, &endptr, 16);
642	if (opt == endptr)
643		offset = 0;
644	load_srec(offset);
645}
646
647static void
648command_fill(char *opt)
649{
650	char	*endptr;
651	char	*p;
652	char	*limit;
653	int	c;
654
655	p = (char *) strtoul(opt, &endptr, 16);
656	if (opt == endptr) {
657		bad_param();
658		return;
659	}
660
661	opt = get_next_arg(opt);
662	limit = (char *) strtoul(opt, &endptr, 16);
663	if (opt == endptr) {
664		bad_param();
665		return;
666	}
667
668	opt = get_next_arg(opt);
669	c = strtoul(opt, &endptr, 16);
670	if (opt == endptr)
671		c = '\0';
672
673	memset(p, c, limit - p);
674}
675
676static void
677check_write_verify_flash(u_int32_t src, u_int32_t dst, size_t len)
678{
679	int		status;
680
681	if ((dst & I28F128_BLOCK_MASK) != 0) {
682		printf("dst addr must be aligned to block boundary (0x%x)\n",
683		       I28F128_BLOCK_SIZE);
684		return;
685	}
686
687	if (i28f128_probe((void *) dst)) {
688		printf("dst addr is not a intel 28F128\n");
689	} else {
690		printf("intel 28F128 detected\n");
691	}
692
693	if ((status = i28f128_region_write((void *) dst, (void *) src, len))
694	    != 0) {
695		printf("write mem to flash failed status = %x\n", status);
696		return;
697	}
698
699	printf("verifying...");
700	if (memcmp((void *) dst, (void *) src, len)) {
701		printf("verify error\n");
702		return;
703	}
704	printf("ok\n");
705
706	printf("writing memory to flash succeeded\n");
707}
708
709static void
710command_write(char *opt)
711{
712	char		*endptr;
713	u_int32_t	src;
714	u_int32_t	dst;
715	size_t		len;
716
717	dst = strtoul(opt, &endptr, 16);
718	if (opt == endptr)
719		goto out;
720
721	opt = get_next_arg(opt);
722	src = strtoul(opt, &endptr, 16);
723	if (opt == endptr)
724		goto out;
725
726	opt = get_next_arg(opt);
727	len = strtoul(opt, &endptr, 16);
728	if (opt == endptr)
729		goto out;
730
731	check_write_verify_flash(src, dst, len);
732	return;
733
734out:
735	bad_param();
736	return;
737}
738
739static void
740command_option(char *subcmd)
741{
742	char	*opt;
743	int	i;
744
745	opt = get_next_arg(subcmd);
746
747	/* dispatch subcommand */
748	for (i = 0; opt_subcommands[i].c_name != NULL; i++) {
749		if (strcmp(subcmd, opt_subcommands[i].c_name) == 0) {
750			opt_subcommands[i].c_fn(opt);
751			break;
752		}
753	}
754	if (opt_subcommands[i].c_name == NULL) {
755		printf("unknown option subcommand\n");
756		command_help(NULL);
757	}
758}
759
760static void
761opt_subcmd_print(char *opt)
762{
763	printf("boot options:\n"
764	       "magic:\t\t%s\n"
765	       "pathname:\t`%s'\n"
766	       "bootp:\t\t%s\n",
767	       bootopts.b_magic == BOOTOPT_MAGIC ? "ok" : "bad",
768	       bootopts.b_pathname,
769	       bootopts.b_flags & B_F_USE_BOOTP ? "yes" : "no");
770	printf("remote IP:\t%s\n", inet_ntoa(bootopts.b_remote_ip));
771	printf("local IP:\t%s\n", inet_ntoa(bootopts.b_local_ip));
772	printf("netmask:\t%s\n", intoa(bootopts.b_netmask));
773	printf("gateway IP:\t%s\n", inet_ntoa(bootopts.b_gate_ip));
774}
775
776static void
777opt_subcmd_read(char *opt)
778{
779	bootopts = *((struct boot_option *) BOOTOPTS_BASE);
780	if (bootopts.b_magic != BOOTOPT_MAGIC)
781		bootopts.b_pathname[0] = '\0';
782}
783
784static void
785opt_subcmd_write(char *opt)
786{
787	bootopts.b_magic = BOOTOPT_MAGIC;
788
789	check_write_verify_flash((u_int32_t) &bootopts, BOOTOPTS_BASE,
790				 sizeof bootopts);
791}
792
793static void
794opt_subcmd_path(char *opt)
795{
796	strlcpy(bootopts.b_pathname, opt, sizeof bootopts.b_pathname);
797}
798
799static void
800opt_subcmd_bootp(char *opt)
801{
802	if (strcmp(opt, "yes") == 0) {
803		bootopts.b_flags |= B_F_USE_BOOTP;
804	} else if (strcmp(opt, "no") == 0) {
805		bootopts.b_flags &= ~B_F_USE_BOOTP;
806	} else {
807		bad_param();
808	}
809}
810
811static void
812opt_subcmd_ip(char *opt)
813{
814	bootopts.b_remote_ip.s_addr = inet_addr(opt);
815	opt = get_next_arg(opt);
816	bootopts.b_local_ip.s_addr = inet_addr(opt);
817	opt = get_next_arg(opt);
818	bootopts.b_netmask = inet_addr(opt);
819	opt = get_next_arg(opt);
820	bootopts.b_gate_ip.s_addr = inet_addr(opt);
821}
822
823static void
824bootmenu(void)
825{
826	char	input[LINEBUFLEN];
827	char	*cmd;
828	char	*opt;
829	int	i;
830
831	for (;;) {
832
833		/* input a line */
834		input[0] = '\0';
835		printf("> ");
836		kgets(input, sizeof(input));
837		cmd = input;
838
839		/* skip leading whitespace. */
840		while(*cmd == ' ')
841			cmd++;
842
843		if(*cmd) {
844			/* here, some command entered */
845
846			opt = get_next_arg(cmd);
847
848			/* dispatch command */
849			for (i = 0; commands[i].c_name != NULL; i++) {
850				if (strcmp(cmd, commands[i].c_name) == 0) {
851					commands[i].c_fn(opt);
852					break;
853				}
854			}
855			if (commands[i].c_name == NULL) {
856				printf("unknown command\n");
857				command_help(NULL);
858			}
859		}
860
861	}
862}
863
864static char
865awaitkey(void)
866{
867	int	i;
868	int	j;
869	char	c = 0;
870
871	while (ISKEY)
872		getchar();
873
874	for (i = BOOTTIMEOUT; i > 0; i--) {
875		printf("%d\b", i);
876		for (j = 0; j < 1000000; j++) {
877			if (ISKEY) {
878				while (ISKEY)
879					c = getchar();
880				goto out;
881			}
882		}
883	}
884
885out:
886	printf("0\n");
887	return(c);
888}
889
890__dead void
891_rtt(void)
892{
893	for (;;)
894		;
895}
896
897int
898main(void)
899{
900	char	c;
901
902	init_devices();
903
904	comcninit();
905
906	opt_subcmd_read(NULL);
907
908	print_banner();
909
910	c = awaitkey();
911	if (c != '\r' && c != '\n' && c != '\0') {
912		printf("type \"?\" or \"h\" for help.\n");
913		bootmenu(); /* does not return */
914	}
915
916	command_boot(NULL);
917	/*
918	 * command_boot() returns only if it failed to boot.
919	 * we enter to boot menu in this case.
920	 */
921	bootmenu();
922
923	return 0;
924}
925