main.c revision 1.24
1/* $NetBSD: main.c,v 1.24 2014/03/22 18:54:28 jakllsch Exp $ */
2
3/*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tohru Nishimura.
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/param.h>
33#include <sys/reboot.h>
34
35#include <lib/libsa/stand.h>
36#include <lib/libsa/loadfile.h>
37#include <lib/libkern/libkern.h>
38
39#include <machine/bootinfo.h>
40
41#include "globals.h"
42
43static const struct bootarg {
44	const char *name;
45	int value;
46} bootargs[] = {
47	{ "multi",	RB_AUTOBOOT },
48	{ "auto",	RB_AUTOBOOT },
49	{ "ask",	RB_ASKNAME },
50	{ "single",	RB_SINGLE },
51	{ "ddb",	RB_KDB },
52	{ "userconf",	RB_USERCONF },
53	{ "norm",	AB_NORMAL },
54	{ "quiet",	AB_QUIET },
55	{ "verb",	AB_VERBOSE },
56	{ "silent",	AB_SILENT },
57	{ "debug",	AB_DEBUG },
58	{ "altboot",	-1 }
59};
60
61/* default PATA drive configuration is "10": single master on first channel */
62static char *drive_config = "10";
63
64void *bootinfo; /* low memory reserved to pass bootinfo structures */
65int bi_size;	/* BOOTINFO_MAXSIZE */
66char *bi_next;
67
68void bi_init(void *);
69void bi_add(void *, int, int);
70
71struct btinfo_memory bi_mem;
72struct btinfo_console bi_cons;
73struct btinfo_clock bi_clk;
74struct btinfo_prodfamily bi_fam;
75struct btinfo_bootpath bi_path;
76struct btinfo_rootdevice bi_rdev;
77struct btinfo_net bi_net;
78struct btinfo_modulelist *btinfo_modulelist;
79size_t btinfo_modulelist_size;
80
81struct boot_module {
82	char *bm_kmod;
83	ssize_t bm_len;
84	struct boot_module *bm_next;
85};
86struct boot_module *boot_modules;
87char module_base[80];
88uint32_t kmodloadp;
89int modules_enabled = 0;
90
91void module_add(const char *);
92void module_load(const char *);
93int module_open(struct boot_module *);
94
95void main(int, char **, char *, char *);
96
97extern char bootprog_name[], bootprog_rev[];
98extern char newaltboot[], newaltboot_end[];
99
100struct pcidev lata[2];
101struct pcidev lnif[2];
102struct pcidev lusb[3];
103int nata, nnif, nusb;
104
105int brdtype;
106uint32_t busclock, cpuclock;
107
108static int check_bootname(char *);
109static int input_cmdline(char **, int);
110static int parse_cmdline(char **, int, char *, char *);
111static int is_space(char);
112#ifdef DEBUG
113static void sat_test(void);
114static void findflash(void);
115#endif
116
117#define	BNAME_DEFAULT "wd0:"
118#define MAX_ARGS 10
119
120void
121main(int argc, char *argv[], char *bootargs_start, char *bootargs_end)
122{
123	unsigned long marks[MARK_MAX];
124	struct brdprop *brdprop;
125	char *new_argv[MAX_ARGS];
126	char *bname;
127	ssize_t len;
128	int err, fd, howto, i, n;
129
130	printf("\n>> %s altboot, revision %s\n", bootprog_name, bootprog_rev);
131
132	brdprop = brd_lookup(brdtype);
133	printf(">> %s, cpu %u MHz, bus %u MHz, %dMB SDRAM\n", brdprop->verbose,
134	    cpuclock / 1000000, busclock / 1000000, bi_mem.memsize >> 20);
135
136	nata = pcilookup(PCI_CLASS_IDE, lata, 2);
137	if (nata == 0)
138		nata = pcilookup(PCI_CLASS_RAID, lata, 2);
139	if (nata == 0)
140		nata = pcilookup(PCI_CLASS_MISCSTORAGE, lata, 2);
141	if (nata == 0)
142		nata = pcilookup(PCI_CLASS_SCSI, lata, 2);
143	nnif = pcilookup(PCI_CLASS_ETH, lnif, 2);
144	nusb = pcilookup(PCI_CLASS_USB, lusb, 3);
145
146#ifdef DEBUG
147	if (nata == 0)
148		printf("No IDE/SATA found\n");
149	else for (n = 0; n < nata; n++) {
150		int b, d, f, bdf, pvd;
151		bdf = lata[n].bdf;
152		pvd = lata[n].pvd;
153		pcidecomposetag(bdf, &b, &d, &f);
154		printf("%04x.%04x DSK %02d:%02d:%02d\n",
155		    PCI_VENDOR(pvd), PCI_PRODUCT(pvd), b, d, f);
156	}
157	if (nnif == 0)
158		printf("no NET found\n");
159	else for (n = 0; n < nnif; n++) {
160		int b, d, f, bdf, pvd;
161		bdf = lnif[n].bdf;
162		pvd = lnif[n].pvd;
163		pcidecomposetag(bdf, &b, &d, &f);
164		printf("%04x.%04x NET %02d:%02d:%02d\n",
165		    PCI_VENDOR(pvd), PCI_PRODUCT(pvd), b, d, f);
166	}
167	if (nusb == 0)
168		printf("no USB found\n");
169	else for (n = 0; n < nusb; n++) {
170		int b, d, f, bdf, pvd;
171		bdf = lusb[0].bdf;
172		pvd = lusb[0].pvd;
173		pcidecomposetag(bdf, &b, &d, &f);
174		printf("%04x.%04x USB %02d:%02d:%02d\n",
175		    PCI_VENDOR(pvd), PCI_PRODUCT(pvd), b, d, f);
176	}
177#endif
178
179	pcisetup();
180	pcifixup();
181
182	/*
183	 * When argc is too big then it is probably a pointer, which could
184	 * indicate that we were launched as a Linux kernel module using
185	 * "bootm".
186	 */
187	if (argc > MAX_ARGS) {
188		if (argv != NULL) {
189			/*
190			 * initrd image was loaded:
191			 * check if it contains a valid altboot command line
192			 */
193			char *p = (char *)argv;
194
195			if (strncmp(p, "altboot:", 8) == 0) {
196				*p = 0;
197				for (p = p + 8; *p >= ' '; p++);
198				argc = parse_cmdline(new_argv, MAX_ARGS,
199				    ((char *)argv) + 8, p);
200				argv = new_argv;
201			} else
202				argc = 0;	/* boot default */
203		} else {
204			/* parse standard Linux bootargs */
205			argc = parse_cmdline(new_argv, MAX_ARGS,
206			    bootargs_start, bootargs_end);
207			argv = new_argv;
208		}
209	}
210
211	/* look for a PATA drive configuration string under the arguments */
212	for (n = 1; n < argc; n++) {
213		if (strncmp(argv[n], "ide:", 4) == 0 &&
214		    argv[n][4] >= '0' && argv[n][4] <= '2') {
215			drive_config = &argv[n][4];
216			break;
217		}
218	}
219
220	/* intialize a disk driver */
221	for (i = 0, n = 0; i < nata; i++)
222		n += dskdv_init(&lata[i]);
223	if (n == 0)
224		printf("IDE/SATA device driver was not found\n");
225
226	/* initialize a network interface */
227	for (n = 0; n < nnif; n++)
228		if (netif_init(&lnif[n]) != 0)
229			break;
230	if (n >= nnif)
231		printf("no NET device driver was found\n");
232
233	/* wait 2s for user to enter interactive mode */
234	for (n = 200; n >= 0; n--) {
235		if (n % 100 == 0)
236			printf("\rHit any key to enter interactive mode: %d",
237			    n / 100);
238		if (tstchar()) {
239#ifdef DEBUG
240			unsigned c;
241
242			c = toupper(getchar());
243			if (c == 'C') {
244				/* controller test terminal */
245				sat_test();
246				n = 200;
247				continue;
248			}
249			else if (c == 'F') {
250				/* find strings in Flash ROM */
251				findflash();
252				n = 200;
253				continue;
254			}
255#else
256			(void)getchar();
257#endif
258			/* enter command line */
259			argv = new_argv;
260			argc = input_cmdline(argv, MAX_ARGS);
261			break;
262		}
263		delay(10000);
264	}
265	putchar('\n');
266
267	howto = RB_AUTOBOOT;		/* default is autoboot = 0 */
268
269	/* get boot options and determine bootname */
270	for (n = 1; n < argc; n++) {
271		if (strncmp(argv[n], "ide:", 4) == 0)
272			continue; /* ignore drive configuration argument */
273
274		for (i = 0; i < sizeof(bootargs) / sizeof(bootargs[0]); i++) {
275			if (strncasecmp(argv[n], bootargs[i].name,
276			    strlen(bootargs[i].name)) == 0) {
277				howto |= bootargs[i].value;
278				break;
279			}
280		}
281		if (i >= sizeof(bootargs) / sizeof(bootargs[0]))
282			break;	/* break on first unknown string */
283	}
284
285	/*
286	 * If no device name is given, we construct a list of drives
287	 * which have valid disklabels.
288	 */
289	if (n >= argc) {
290		n = 0;
291		argc = 0;
292		argv = alloc(MAX_UNITS * (sizeof(char *) + sizeof("wdN:")));
293		bname = (char *)(argv + MAX_UNITS);
294		for (i = 0; i < MAX_UNITS; i++) {
295			if (!dlabel_valid(i))
296				continue;
297			sprintf(bname, "wd%d:", i);
298			argv[argc++] = bname;
299			bname += sizeof("wdN:");
300		}
301		/* use default drive if no valid disklabel is found */
302		if (argc == 0) {
303			argc = 1;
304			argv[0] = BNAME_DEFAULT;
305		}
306	}
307
308	/* try to boot off kernel from the drive list */
309	while (n < argc) {
310		bname = argv[n++];
311
312		if (check_bootname(bname) == 0) {
313			printf("%s not a valid bootname\n", bname);
314			continue;
315		}
316
317		if ((fd = open(bname, 0)) < 0) {
318			if (errno == ENOENT)
319				printf("\"%s\" not found\n", bi_path.bootpath);
320			continue;
321		}
322		printf("loading \"%s\" ", bi_path.bootpath);
323		marks[MARK_START] = 0;
324
325		if (howto == -1) {
326			/* load another altboot binary and replace ourselves */
327			len = read(fd, (void *)0x100000, 0x1000000 - 0x100000);
328			if (len == -1)
329				goto loadfail;
330			close(fd);
331			netif_shutdown_all();
332
333			memcpy((void *)0xf0000, newaltboot,
334			    newaltboot_end - newaltboot);
335			__syncicache((void *)0xf0000,
336			    newaltboot_end - newaltboot);
337			printf("Restarting...\n");
338			run((void *)1, argv, (void *)0x100000, (void *)len,
339			    (void *)0xf0000);
340		}
341
342		err = fdloadfile(fd, marks, LOAD_KERNEL);
343		close(fd);
344		if (err < 0)
345			continue;
346
347		printf("entry=%p, ssym=%p, esym=%p\n",
348		    (void *)marks[MARK_ENTRY],
349		    (void *)marks[MARK_SYM],
350		    (void *)marks[MARK_END]);
351
352		bootinfo = (void *)0x4000;
353		bi_init(bootinfo);
354		bi_add(&bi_cons, BTINFO_CONSOLE, sizeof(bi_cons));
355		bi_add(&bi_mem, BTINFO_MEMORY, sizeof(bi_mem));
356		bi_add(&bi_clk, BTINFO_CLOCK, sizeof(bi_clk));
357		bi_add(&bi_path, BTINFO_BOOTPATH, sizeof(bi_path));
358		bi_add(&bi_rdev, BTINFO_ROOTDEVICE, sizeof(bi_rdev));
359		bi_add(&bi_fam, BTINFO_PRODFAMILY, sizeof(bi_fam));
360		if (brdtype == BRD_SYNOLOGY || brdtype == BRD_DLINKDSM) {
361			/* need to pass this MAC address to kernel */
362			bi_add(&bi_net, BTINFO_NET, sizeof(bi_net));
363		}
364
365		if (modules_enabled) {
366			if (fsmod != NULL)
367				module_add(fsmod);
368			kmodloadp = marks[MARK_END];
369			btinfo_modulelist = NULL;
370			module_load(bname);
371			if (btinfo_modulelist != NULL &&
372			    btinfo_modulelist->num > 0)
373				bi_add(btinfo_modulelist, BTINFO_MODULELIST,
374				    btinfo_modulelist_size);
375		}
376
377		launchfixup();
378		netif_shutdown_all();
379
380		__syncicache((void *)marks[MARK_ENTRY],
381		    (u_int)marks[MARK_SYM] - (u_int)marks[MARK_ENTRY]);
382
383		run((void *)marks[MARK_SYM], (void *)marks[MARK_END],
384		    (void *)howto, bootinfo, (void *)marks[MARK_ENTRY]);
385
386		/* should never come here */
387		printf("exec returned. Restarting...\n");
388		_rtt();
389	}
390  loadfail:
391	printf("load failed. Restarting...\n");
392	_rtt();
393}
394
395void
396bi_init(void *addr)
397{
398	struct btinfo_magic bi_magic;
399
400	memset(addr, 0, BOOTINFO_MAXSIZE);
401	bi_next = (char *)addr;
402	bi_size = 0;
403
404	bi_magic.magic = BOOTINFO_MAGIC;
405	bi_add(&bi_magic, BTINFO_MAGIC, sizeof(bi_magic));
406}
407
408void
409bi_add(void *new, int type, int size)
410{
411	struct btinfo_common *bi;
412
413	if (bi_size + size > BOOTINFO_MAXSIZE)
414		return;				/* XXX error? */
415
416	bi = new;
417	bi->next = size;
418	bi->type = type;
419	memcpy(bi_next, new, size);
420	bi_next += size;
421}
422
423void
424module_add(const char *name)
425{
426	struct boot_module *bm, *bmp;
427
428	while (*name == ' ' || *name == '\t')
429		++name;
430
431	bm = alloc(sizeof(struct boot_module) + strlen(name) + 1);
432	if (bm == NULL) {
433		printf("couldn't allocate module %s\n", name);
434		return;
435	}
436
437	bm->bm_kmod = (char *)(bm + 1);
438	bm->bm_len = -1;
439	bm->bm_next = NULL;
440	strcpy(bm->bm_kmod, name);
441	if ((bmp = boot_modules) == NULL)
442		boot_modules = bm;
443	else {
444		while (bmp->bm_next != NULL)
445			bmp = bmp->bm_next;
446		bmp->bm_next = bm;
447	}
448}
449
450#define PAGE_SIZE	4096
451#define alignpg(x)	(((x)+PAGE_SIZE-1) & ~(PAGE_SIZE-1))
452
453void
454module_load(const char *kernel_path)
455{
456	struct boot_module *bm;
457	struct bi_modulelist_entry *bi;
458	struct stat st;
459	char *p;
460	int size, fd;
461
462	strcpy(module_base, kernel_path);
463	if ((p = strchr(module_base, ':')) == NULL)
464		return; /* eeh?! */
465	p += 1;
466	size = sizeof(module_base) - (p - module_base);
467
468	if (netbsd_version / 1000000 % 100 == 99) {
469		/* -current */
470		snprintf(p, size,
471		    "/stand/sandpoint/%d.%d.%d/modules",
472		    netbsd_version / 100000000,
473		    netbsd_version / 1000000 % 100,
474		    netbsd_version / 100 % 100);
475	}
476	 else if (netbsd_version != 0) {
477		/* release */
478		snprintf(p, size,
479		    "/stand/sandpoint/%d.%d/modules",
480		    netbsd_version / 100000000,
481		    netbsd_version / 1000000 % 100);
482	}
483
484	/*
485	 * 1st pass; determine module existence
486	 */
487	size = 0;
488	for (bm = boot_modules; bm != NULL; bm = bm->bm_next) {
489		fd = module_open(bm);
490		if (fd == -1)
491			continue;
492		if (fstat(fd, &st) == -1 || st.st_size == -1) {
493			printf("WARNING: couldn't stat %s\n", bm->bm_kmod);
494			close(fd);
495			continue;
496		}
497		bm->bm_len = (int)st.st_size;
498		close(fd);
499		size += sizeof(struct bi_modulelist_entry);
500	}
501	if (size == 0)
502		return;
503
504	size += sizeof(struct btinfo_modulelist);
505	btinfo_modulelist = alloc(size);
506	if (btinfo_modulelist == NULL) {
507		printf("WARNING: couldn't allocate module list\n");
508		return;
509	}
510	btinfo_modulelist_size = size;
511	btinfo_modulelist->num = 0;
512
513	/*
514	 * 2nd pass; load modules into memory
515	 */
516	kmodloadp = alignpg(kmodloadp);
517	bi = (struct bi_modulelist_entry *)(btinfo_modulelist + 1);
518	for (bm = boot_modules; bm != NULL; bm = bm->bm_next) {
519		if (bm->bm_len == -1)
520			continue; /* already found unavailable */
521		fd = module_open(bm);
522		printf("module \"%s\" ", bm->bm_kmod);
523		size = read(fd, (char *)kmodloadp, SSIZE_MAX);
524		if (size < bm->bm_len)
525			printf("WARNING: couldn't load");
526		else {
527			snprintf(bi->kmod, sizeof(bi->kmod), bm->bm_kmod);
528			bi->type = BI_MODULE_ELF;
529			bi->len = size;
530			bi->base = kmodloadp;
531			btinfo_modulelist->num += 1;
532			printf("loaded at 0x%08x size 0x%x", kmodloadp, size);
533			kmodloadp += alignpg(size);
534			bi += 1;
535		}
536		printf("\n");
537		close(fd);
538	}
539	btinfo_modulelist->endpa = kmodloadp;
540}
541
542int
543module_open(struct boot_module *bm)
544{
545	char path[80];
546	int fd;
547
548	snprintf(path, sizeof(path),
549	    "%s/%s/%s.kmod", module_base, bm->bm_kmod, bm->bm_kmod);
550	fd = open(path, 0);
551	return fd;
552}
553
554/*
555 * Return the drive configuration for the requested channel 'ch'.
556 * Channel 2 is the first channel of the next IDE controller.
557 * 0: for no drive present on channel
558 * 1: for master drive present on channel, no slave
559 * 2: for master and slave drive present
560 */
561int
562get_drive_config(int ch)
563{
564	if (drive_config != NULL) {
565		if (strlen(drive_config) <= ch)
566			return 0;	/* an unspecified channel is unused */
567		if (drive_config[ch] >= '0' && drive_config[ch] <= '2')
568			return drive_config[ch] - '0';
569	}
570	return -1;
571}
572
573void *
574allocaligned(size_t size, size_t align)
575{
576	uint32_t p;
577
578	if (align-- < 2)
579		return alloc(size);
580	p = (uint32_t)alloc(size + align);
581	return (void *)((p + align) & ~align);
582}
583
584static int hex2nibble(char c)
585{
586
587	if (c >= 'a')
588		c &= ~0x20;
589	if (c >= 'A' && c <= 'F')
590		c -= 'A' - ('9' + 1);
591	else if (c < '0' || c > '9')
592		return -1;
593
594	return c - '0';
595}
596
597uint32_t
598read_hex(const char *s)
599{
600	int n;
601	uint32_t val;
602
603	val = 0;
604	while ((n = hex2nibble(*s++)) >= 0)
605		val = (val << 4) | n;
606	return val;
607}
608
609static int
610check_bootname(char *s)
611{
612	/*
613	 * nfs:
614	 * nfs:<bootfile>
615	 * tftp:
616	 * tftp:<bootfile>
617	 * wd[N[P]]:<bootfile>
618	 * mem:<address>
619	 *
620	 * net is a synonym of nfs.
621	 */
622	if (strncmp(s, "nfs:", 4) == 0 || strncmp(s, "net:", 4) == 0 ||
623	    strncmp(s, "tftp:", 5) == 0 || strncmp(s, "mem:", 4) == 0)
624		return 1;
625	if (s[0] == 'w' && s[1] == 'd') {
626		s += 2;
627		if (*s != ':' && *s >= '0' && *s <= '3') {
628			++s;
629			if (*s != ':' && *s >= 'a' && *s <= 'p')
630				++s;
631		}
632		return *s == ':';
633	}
634	return 0;
635}
636
637static int input_cmdline(char **argv, int maxargc)
638{
639	char *cmdline;
640
641	printf("\nbootargs> ");
642	cmdline = alloc(256);
643	gets(cmdline);
644
645	return parse_cmdline(argv, maxargc, cmdline,
646	    cmdline + strlen(cmdline));
647}
648
649static int
650parse_cmdline(char **argv, int maxargc, char *p, char *end)
651{
652	int argc;
653
654	argv[0] = "";
655	for (argc = 1; argc < maxargc && p < end; argc++) {
656		while (is_space(*p))
657			p++;
658		if (p >= end)
659			break;
660		argv[argc] = p;
661		while (!is_space(*p) && p < end)
662			p++;
663		*p++ = '\0';
664	}
665
666	return argc;
667}
668
669static int
670is_space(char c)
671{
672
673	return c > '\0' && c <= ' ';
674}
675
676#ifdef DEBUG
677static void
678findflash(void)
679{
680	char buf[256];
681	int i, n;
682	unsigned char c, *p;
683
684	for (;;) {
685		printf("\nfind> ");
686		gets(buf);
687		if (tolower((unsigned)buf[0]) == 'x')
688			break;
689		for (i = 0, n = 0, c = 0; buf[i]; i++) {
690			c <<= 4;
691			c |= hex2nibble(buf[i]);
692			if (i & 1)
693				buf[n++] = c;
694		}
695		printf("Searching for:");
696		for (i = 0; i < n; i++)
697			printf(" %02x", buf[i]);
698		printf("\n");
699		for (p = (unsigned char *)0xff000000;
700		     p <= (unsigned char *)(0xffffffff-n); p++) {
701			for (i = 0; i < n; i++) {
702				if (p[i] != buf[i])
703					break;
704			}
705			if (i >= n)
706				printf("Found at %08x\n", (unsigned)p);
707		}
708	}
709}
710
711static void
712sat_test(void)
713{
714	char buf[1024];
715	int i, j, n, pos;
716	unsigned char c;
717
718	putchar('\n');
719	for (;;) {
720		do {
721			for (pos = 0; pos < 1024 && sat_tstch() != 0; pos++)
722				buf[pos] = sat_getch();
723			if (pos > 1023)
724				break;
725			delay(100000);
726		} while (sat_tstch());
727
728		for (i = 0; i < pos; i += 16) {
729			if ((n = i + 16) > pos)
730				n = pos;
731			for (j = 0; j < n; j++)
732				printf("%02x ", (unsigned)buf[i + j]);
733			for (; j < 16; j++)
734				printf("   ");
735			putchar('\"');
736			for (j = 0; j < n; j++) {
737				c = buf[i + j];
738				putchar((c >= 0x20 && c <= 0x7e) ? c : '.');
739			}
740			printf("\"\n");
741		}
742
743		printf("controller> ");
744		gets(buf);
745		if (buf[0] == '*' && buf[1] == 'X')
746			break;
747
748		if (buf[0] == '0' && tolower((unsigned)buf[1]) == 'x') {
749			for (i = 2, n = 0, c = 0; buf[i]; i++) {
750				c <<= 4;
751				c |= hex2nibble(buf[i]);
752				if (i & 1)
753					buf[n++] = c;
754			}
755		} else
756			n = strlen(buf);
757
758		if (n > 0)
759			sat_write(buf, n);
760	}
761}
762#endif
763