1/*-
2 * Copyright (c) 2008-2009 TAKAHASHI Yoshihiro
3 * Copyright (c) 1998 Robert Nordier
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms are freely
7 * permitted provided that the above copyright notice and this
8 * paragraph and the following disclaimer are duplicated in all
9 * such forms.
10 *
11 * This software is provided "AS IS" and without any express or
12 * implied warranties, including, without limitation, the implied
13 * warranties of merchantability and fitness for a particular
14 * purpose.
15 */
16
17#include <sys/cdefs.h>
18__FBSDID("$FreeBSD: stable/11/stand/pc98/boot2/boot2.c 333049 2018-04-27 02:39:36Z nyan $");
19
20#include <sys/param.h>
21#include <sys/disklabel.h>
22#include <sys/diskpc98.h>
23#include <sys/dirent.h>
24#include <sys/reboot.h>
25
26#include <machine/bootinfo.h>
27#include <machine/cpufunc.h>
28#include <machine/elf.h>
29
30#include <stdarg.h>
31
32#include <a.out.h>
33
34#include <btxv86.h>
35
36#include "boot2.h"
37#include "lib.h"
38#include "paths.h"
39#include "rbx.h"
40
41/* Define to 0 to omit serial support */
42#ifndef SERIAL
43#define SERIAL 0
44#endif
45
46#define IO_KEYBOARD	1
47#define IO_SERIAL	2
48
49#if SERIAL
50#define DO_KBD (ioctrl & IO_KEYBOARD)
51#define DO_SIO (ioctrl & IO_SERIAL)
52#else
53#define DO_KBD (1)
54#define DO_SIO (0)
55#endif
56
57#define SECOND		1	/* Circa that many ticks in a second. */
58
59#define ARGS		0x900
60#define NOPT		14
61#define NDEV		3
62
63#define DRV_DISK	0xf0
64#define DRV_UNIT	0x0f
65
66#define TYPE_AD		0
67#define TYPE_DA		1
68#define TYPE_FD		2
69
70extern uint32_t _end;
71
72static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
73static const unsigned char flags[NOPT] = {
74	RBX_DUAL,
75	RBX_SERIAL,
76	RBX_ASKNAME,
77	RBX_CDROM,
78	RBX_CONFIG,
79	RBX_KDB,
80	RBX_GDB,
81	RBX_MUTE,
82	RBX_NOINTR,
83	RBX_PAUSE,
84	RBX_QUIET,
85	RBX_DFLTROOT,
86	RBX_SINGLE,
87	RBX_VERBOSE
88};
89
90static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
91static const unsigned char dev_maj[NDEV] = {30, 4, 2};
92static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90};
93
94static struct dsk {
95	unsigned daua;
96	unsigned type;
97	unsigned disk;
98	unsigned unit;
99	unsigned head;
100	unsigned sec;
101	uint8_t slice;
102	uint8_t part;
103	unsigned start;
104} dsk;
105static char cmd[512], cmddup[512], knamebuf[1024];
106static const char *kname;
107uint32_t opts;
108static struct bootinfo bootinfo;
109#if SERIAL
110static int comspeed = SIOSPD;
111static uint8_t ioctrl = IO_KEYBOARD;
112#endif
113
114int main(void);
115void exit(int);
116static void load(void);
117static int parse(void);
118static int dskread(void *, unsigned, unsigned);
119static void printf(const char *,...);
120static void putchar(int);
121static int drvread(void *, unsigned);
122static int keyhit(unsigned);
123static int xputc(int);
124static int xgetc(int);
125static inline int getc(int);
126
127static void memcpy(void *, const void *, int);
128static void
129memcpy(void *dst, const void *src, int len)
130{
131	const char *s = src;
132	char *d = dst;
133
134	while (len--)
135		*d++ = *s++;
136}
137
138static inline int
139strcmp(const char *s1, const char *s2)
140{
141
142	for (; *s1 == *s2 && *s1; s1++, s2++);
143	return ((unsigned char)*s1 - (unsigned char)*s2);
144}
145
146#define	UFS_SMALL_CGBASE
147#include "ufsread.c"
148
149static inline int
150xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
151{
152
153	if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
154		printf("Invalid %s\n", "format");
155		return (-1);
156	}
157	return (0);
158}
159
160static inline void
161getstr(void)
162{
163	char *s;
164	int c;
165
166	s = cmd;
167	for (;;) {
168		switch (c = xgetc(0)) {
169		case 0:
170			break;
171		case '\177':
172		case '\b':
173			if (s > cmd) {
174				s--;
175				printf("\b \b");
176			}
177			break;
178		case '\n':
179		case '\r':
180			*s = 0;
181			return;
182		default:
183			if (s - cmd < sizeof(cmd) - 1)
184				*s++ = c;
185			putchar(c);
186		}
187	}
188}
189
190static inline void
191putc(int c)
192{
193
194	v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
195	v86.addr = PUTCORG;		/* call to putc in boot1 */
196	v86.eax = c;
197	v86int();
198	v86.ctl = V86_FLAGS;
199}
200
201static inline int
202is_scsi_hd(void)
203{
204
205    if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01)
206	return 1;
207
208    return 0;
209}
210
211static inline void
212fix_sector_size(void)
213{
214    u_char *p;
215
216    p = (u_char *)PTOV(0x460 + dsk.unit * 4);	/* SCSI equipment parameter */
217
218    if ((p[0] & 0x1f) == 7) {		/* SCSI MO */
219	if (!(p[3] & 0x30)) {		/* 256B / sector */
220	    p[3] |= 0x10;		/* forced set 512B / sector */
221	    p[3 + 0xa1000] |= 0x10;
222	}
223    }
224}
225
226static inline uint32_t
227get_diskinfo(void)
228{
229
230    if (dsk.disk == 0x30) {				/* 1440KB FD */
231	/* 80 cylinders, 2 heads, 18 sectors */
232	return (80 << 16) | (2 << 8) | 18;
233    } else if (dsk.disk == 0x90) {			/* 1200KB FD */
234	/* 80 cylinders, 2 heads, 15 sectors */
235	return (80 << 16) | (2 << 8) | 15;
236    } else if (dsk.disk == 0x80 || is_scsi_hd()) {	/* IDE or SCSI HDD */
237	v86.addr = 0x1b;
238	v86.eax = 0x8400 | dsk.daua;
239	v86int();
240	return (v86.ecx << 16) | v86.edx;
241    }
242
243    /* SCSI MO or CD */
244    fix_sector_size();	/* SCSI MO */
245
246    /* other SCSI devices */
247    return (65535 << 16) | (8 << 8) | 32;
248}
249
250static void
251set_dsk(void)
252{
253    uint32_t di;
254
255    di = get_diskinfo();
256
257    dsk.head = (di >> 8) & 0xff;
258    dsk.sec = di & 0xff;
259    dsk.start = 0;
260}
261
262#ifdef GET_BIOSGEOM
263static uint32_t
264bd_getbigeom(int bunit)
265{
266    int hds = 0;
267    int unit = 0x80;		/* IDE HDD */
268    u_int addr = 0x55d;
269
270    while (unit < 0xa7) {
271	if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
272	    if (hds++ == bunit)
273		break;
274
275	if (unit >= 0xA0) {
276	    int media = ((unsigned *)PTOV(0x460))[unit & 0x0F] & 0x1F;
277
278	    if (media == 7 && hds++ == bunit)	/* SCSI MO */
279		return(0xFFFE0820); /* C:65535 H:8 S:32 */
280	}
281	if (++unit == 0x84) {
282	    unit = 0xA0;	/* SCSI HDD */
283	    addr = 0x482;
284	}
285    }
286    if (unit == 0xa7)
287	return 0x4F020F;	/* 1200KB FD C:80 H:2 S:15 */
288    v86.addr = 0x1b;
289    v86.eax = 0x8400 | unit;
290    v86int();
291    if (V86_CY(v86.efl))
292	return 0x4F020F;	/* 1200KB FD C:80 H:2 S:15 */
293    return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
294}
295#endif
296
297static int
298check_slice(void)
299{
300    struct pc98_partition *dp;
301    char *sec;
302    unsigned i, cyl;
303
304    sec = dmadat->secbuf;
305    cyl = *(uint16_t *)PTOV(ARGS);
306    set_dsk();
307
308    if (dsk.type == TYPE_FD)
309	return (WHOLE_DISK_SLICE);
310    if (drvread(sec, PC98_BBSECTOR))
311	return (WHOLE_DISK_SLICE);	/* Read error */
312    dp = (void *)(sec + PC98_PARTOFF);
313    for (i = 0; i < PC98_NPARTS; i++) {
314	if (dp[i].dp_mid == DOSMID_386BSD) {
315	    if (dp[i].dp_scyl <= cyl && cyl <= dp[i].dp_ecyl)
316		return (BASE_SLICE + i);
317	}
318    }
319
320    return (WHOLE_DISK_SLICE);
321}
322
323int
324main(void)
325{
326#ifdef GET_BIOSGEOM
327	int i;
328#endif
329	uint8_t autoboot;
330	ufs_ino_t ino;
331	size_t nbyte;
332
333	dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
334	v86.ctl = V86_FLAGS;
335	v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
336	dsk.daua = *(uint8_t *)PTOV(0x584);
337	dsk.disk = dsk.daua & DRV_DISK;
338	dsk.unit = dsk.daua & DRV_UNIT;
339	if (dsk.disk == 0x80)
340		dsk.type = TYPE_AD;
341	else if (dsk.disk == 0xa0)
342		dsk.type = TYPE_DA;
343	else /* if (dsk.disk == 0x30 || dsk.disk == 0x90) */
344		dsk.type = TYPE_FD;
345	dsk.slice = check_slice();
346#ifdef GET_BIOSGEOM
347	for (i = 0; i < N_BIOS_GEOM; i++)
348		bootinfo.bi_bios_geom[i] = bd_getbigeom(i);
349#endif
350	bootinfo.bi_version = BOOTINFO_VERSION;
351	bootinfo.bi_size = sizeof(bootinfo);
352
353	/* Process configuration file */
354
355	autoboot = 1;
356
357	if ((ino = lookup(PATH_CONFIG)) ||
358	    (ino = lookup(PATH_DOTCONFIG))) {
359		nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
360		cmd[nbyte] = '\0';
361	}
362
363	if (*cmd) {
364		memcpy(cmddup, cmd, sizeof(cmd));
365		if (parse())
366			autoboot = 0;
367		if (!OPT_CHECK(RBX_QUIET))
368			printf("%s: %s", PATH_CONFIG, cmddup);
369		/* Do not process this command twice */
370		*cmd = 0;
371	}
372
373	/*
374	 * Try to exec stage 3 boot loader. If interrupted by a keypress,
375	 * or in case of failure, try to load a kernel directly instead.
376	 */
377
378	if (!kname) {
379		kname = PATH_LOADER;
380		if (autoboot && !keyhit(3*SECOND)) {
381			load();
382			kname = PATH_KERNEL;
383		}
384	}
385
386	/* Present the user with the boot2 prompt. */
387
388	for (;;) {
389		if (!autoboot || !OPT_CHECK(RBX_QUIET))
390			printf("\nFreeBSD/pc98 boot\n"
391				 "Default: %u:%s(%u,%c)%s\n"
392				 "boot: ",
393			    dsk.unit, dev_nm[dsk.type], dsk.unit,
394			    'a' + dsk.part, kname);
395		if (DO_SIO)
396			sio_flush();
397		if (!autoboot || keyhit(3*SECOND))
398			getstr();
399		else if (!autoboot || !OPT_CHECK(RBX_QUIET))
400			putchar('\n');
401		autoboot = 0;
402		if (parse())
403			putchar('\a');
404		else
405			load();
406	}
407}
408
409/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
410void
411exit(int x)
412{
413
414}
415
416static void
417load(void)
418{
419	union {
420		struct exec ex;
421		Elf32_Ehdr eh;
422	} hdr;
423	static Elf32_Phdr ep[2];
424	static Elf32_Shdr es[2];
425	caddr_t p;
426	ufs_ino_t ino;
427	uint32_t addr;
428	int k;
429	uint8_t i, j;
430
431	if (!(ino = lookup(kname))) {
432		if (!ls)
433			printf("No %s\n", kname);
434		return;
435	}
436	if (xfsread(ino, &hdr, sizeof(hdr)))
437		return;
438
439	if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
440		addr = hdr.ex.a_entry & 0xffffff;
441		p = PTOV(addr);
442		fs_off = PAGE_SIZE;
443		if (xfsread(ino, p, hdr.ex.a_text))
444			return;
445		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
446		if (xfsread(ino, p, hdr.ex.a_data))
447			return;
448	} else if (IS_ELF(hdr.eh)) {
449		fs_off = hdr.eh.e_phoff;
450		for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) {
451			if (xfsread(ino, ep + j, sizeof(ep[0])))
452				return;
453			if (ep[j].p_type == PT_LOAD)
454				j++;
455		}
456		for (i = 0; i < 2; i++) {
457			p = PTOV(ep[i].p_paddr & 0xffffff);
458			fs_off = ep[i].p_offset;
459			if (xfsread(ino, p, ep[i].p_filesz))
460				return;
461		}
462		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
463		bootinfo.bi_symtab = VTOP(p);
464		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
465			fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
466			    (hdr.eh.e_shstrndx + 1);
467			if (xfsread(ino, &es, sizeof(es)))
468				return;
469			for (i = 0; i < 2; i++) {
470				*(Elf32_Word *)p = es[i].sh_size;
471				p += sizeof(es[i].sh_size);
472				fs_off = es[i].sh_offset;
473				if (xfsread(ino, p, es[i].sh_size))
474					return;
475				p += es[i].sh_size;
476			}
477		}
478		addr = hdr.eh.e_entry & 0xffffff;
479		bootinfo.bi_esymtab = VTOP(p);
480	} else {
481		printf("Invalid %s\n", "format");
482		return;
483	}
484
485	bootinfo.bi_kernelname = VTOP(kname);
486	bootinfo.bi_bios_dev = dsk.daua;
487	__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
488	    MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
489	    0, 0, 0, VTOP(&bootinfo));
490}
491
492static int
493parse()
494{
495	char *arg = cmd;
496	char *ep, *p, *q;
497	const char *cp;
498	unsigned int drv;
499	int c, i, j;
500	size_t k;
501
502	while ((c = *arg++)) {
503		if (c == ' ' || c == '\t' || c == '\n')
504			continue;
505		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
506		ep = p;
507		if (*p)
508			*p++ = 0;
509		if (c == '-') {
510			while ((c = *arg++)) {
511				if (c == 'P') {
512					if (*(uint8_t *)PTOV(0x481) & 0x48) {
513						cp = "yes";
514					} else {
515						opts |= OPT_SET(RBX_DUAL) |
516						    OPT_SET(RBX_SERIAL);
517						cp = "no";
518					}
519					printf("Keyboard: %s\n", cp);
520					continue;
521#if SERIAL
522				} else if (c == 'S') {
523					j = 0;
524					while ((unsigned int)(i = *arg++ - '0') <= 9)
525						j = j * 10 + i;
526					if (j > 0 && i == -'0') {
527						comspeed = j;
528						break;
529					}
530					/*
531					 * Fall through to error below
532					 * ('S' not in optstr[]).
533					 */
534#endif
535				}
536				for (i = 0; c != optstr[i]; i++)
537					if (i == NOPT - 1)
538						return (-1);
539				opts ^= OPT_SET(flags[i]);
540			}
541#if SERIAL
542			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
543			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
544			if (DO_SIO) {
545				if (sio_init(115200 / comspeed) != 0)
546					ioctrl &= ~IO_SERIAL;
547			}
548#endif
549		} else {
550			for (q = arg--; *q && *q != '('; q++);
551			if (*q) {
552				drv = -1;
553				if (arg[1] == ':') {
554					drv = *arg - '0';
555					if (drv > 9)
556						return (-1);
557					arg += 2;
558				}
559				if (q - arg != 2)
560					return (-1);
561				for (i = 0; arg[0] != dev_nm[i][0] ||
562				    arg[1] != dev_nm[i][1]; i++)
563					if (i == NDEV - 1)
564						return (-1);
565				dsk.type = i;
566				arg += 3;
567				dsk.unit = *arg - '0';
568				if (arg[1] != ',' || dsk.unit > 9)
569					return (-1);
570				arg += 2;
571				dsk.slice = WHOLE_DISK_SLICE;
572				if (arg[1] == ',') {
573					dsk.slice = *arg - '0' + 1;
574					if (dsk.slice > PC98_NPARTS + 1)
575						return (-1);
576					arg += 2;
577				}
578				if (arg[1] != ')')
579					return (-1);
580				dsk.part = *arg - 'a';
581				if (dsk.part > 7)
582					return (-1);
583				arg += 2;
584				if (drv == -1)
585					drv = dsk.unit;
586				dsk.disk = dev_daua[dsk.type];
587				dsk.daua = dsk.disk | dsk.unit;
588				dsk_meta = 0;
589			}
590			k = ep - arg;
591			if (k > 0) {
592				if (k >= sizeof(knamebuf))
593					return (-1);
594				memcpy(knamebuf, arg, k + 1);
595				kname = knamebuf;
596			}
597		}
598		arg = p;
599	}
600	return (0);
601}
602
603static int
604dskread(void *buf, unsigned lba, unsigned nblk)
605{
606	struct pc98_partition *dp;
607	struct disklabel *d;
608	char *sec;
609	unsigned i;
610	uint8_t sl;
611	u_char *p;
612	const char *reason;
613
614	if (!dsk_meta) {
615		sec = dmadat->secbuf;
616		set_dsk();
617		if (dsk.type == TYPE_FD)
618			goto unsliced;
619		if (drvread(sec, PC98_BBSECTOR))
620			return -1;
621		dp = (void *)(sec + PC98_PARTOFF);
622		sl = dsk.slice;
623		if (sl < BASE_SLICE) {
624			for (i = 0; i < PC98_NPARTS; i++)
625				if (dp[i].dp_mid == DOSMID_386BSD) {
626					sl = BASE_SLICE + i;
627					break;
628				}
629			dsk.slice = sl;
630		}
631		if (sl != WHOLE_DISK_SLICE) {
632			dp += sl - BASE_SLICE;
633			if (dp->dp_mid != DOSMID_386BSD) {
634				reason = "slice";
635				goto error;
636			}
637			dsk.start = dp->dp_scyl * dsk.head * dsk.sec +
638				dp->dp_shd * dsk.sec + dp->dp_ssect;
639		}
640		if (drvread(sec, dsk.start + LABELSECTOR))
641			return -1;
642		d = (void *)(sec + LABELOFFSET);
643		if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
644			if (dsk.part != RAW_PART) {
645				reason = "label";
646				goto error;
647			}
648		} else {
649			if (dsk.part >= d->d_npartitions ||
650			    !d->d_partitions[dsk.part].p_size) {
651				reason = "partition";
652				goto error;
653			}
654			dsk.start += d->d_partitions[dsk.part].p_offset;
655			dsk.start -= d->d_partitions[RAW_PART].p_offset;
656		}
657	unsliced: ;
658	}
659	for (p = buf; nblk; p += 512, lba++, nblk--) {
660		if ((i = drvread(p, dsk.start + lba)))
661			return (i);
662	}
663	return (0);
664error:
665	printf("Invalid %s\n", reason);
666	return (-1);
667}
668
669static void
670printf(const char *fmt,...)
671{
672	va_list ap;
673	static char buf[10];
674	char *s;
675	unsigned u;
676	int c;
677
678	va_start(ap, fmt);
679	while ((c = *fmt++)) {
680		if (c == '%') {
681			c = *fmt++;
682			switch (c) {
683			case 'c':
684				putchar(va_arg(ap, int));
685				continue;
686			case 's':
687				for (s = va_arg(ap, char *); *s; s++)
688					putchar(*s);
689				continue;
690			case 'u':
691				u = va_arg(ap, unsigned);
692				s = buf;
693				do
694					*s++ = '0' + u % 10U;
695				while (u /= 10U);
696				while (--s >= buf)
697					putchar(*s);
698				continue;
699			}
700		}
701		putchar(c);
702	}
703	va_end(ap);
704	return;
705}
706
707static void
708putchar(int c)
709{
710
711	if (c == '\n')
712		xputc('\r');
713	xputc(c);
714}
715
716static int
717drvread(void *buf, unsigned lba)
718{
719	static unsigned c = 0x2d5c7c2f;
720	unsigned bpc, x, cyl, head, sec;
721
722	bpc = dsk.sec * dsk.head;
723	cyl = lba / bpc;
724	x = lba % bpc;
725	head = x / dsk.sec;
726	sec = x % dsk.sec;
727
728	if (!OPT_CHECK(RBX_QUIET)) {
729		xputc(c = c << 8 | c >> 24);
730		xputc('\b');
731	}
732	v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
733	v86.addr = READORG;		/* call to read in boot1 */
734	v86.ecx = cyl;
735	v86.edx = (head << 8) | sec;
736	v86.edi = lba;
737	v86.ebx = 512;
738	v86.es = VTOPSEG(buf);
739	v86.ebp = VTOPOFF(buf);
740	v86int();
741	v86.ctl = V86_FLAGS;
742	if (V86_CY(v86.efl)) {
743		printf("error %u c/h/s %u/%u/%u lba %u\n", v86.eax >> 8 & 0xff,
744		    cyl, head, sec, lba);
745		return (-1);
746	}
747	return (0);
748}
749
750static inline void
751delay(void)
752{
753	int i;
754
755	i = 800;
756	do {
757		outb(0x5f, 0);	/* about 600ns */
758	} while (--i >= 0);
759}
760
761static int
762keyhit(unsigned sec)
763{
764	unsigned i;
765
766	if (OPT_CHECK(RBX_NOINTR))
767		return (0);
768	for (i = 0; i < sec * 1000; i++) {
769		if (xgetc(1))
770			return (1);
771		delay();
772	}
773	return (0);
774}
775
776static int
777xputc(int c)
778{
779
780	if (DO_KBD)
781		putc(c);
782	if (DO_SIO)
783		sio_putc(c);
784	return (c);
785}
786
787static int
788getc(int fn)
789{
790
791	v86.addr = 0x18;
792	v86.eax = fn << 8;
793	v86int();
794	if (fn)
795		return ((v86.ebx >> 8) & 0x01);
796	else
797		return (v86.eax & 0xff);
798}
799
800static int
801xgetc(int fn)
802{
803
804	if (OPT_CHECK(RBX_NOINTR))
805		return (0);
806	for (;;) {
807		if (DO_KBD && getc(1))
808			return (fn ? 1 : getc(0));
809		if (DO_SIO && sio_ischar())
810			return (fn ? 1 : sio_getc());
811		if (fn)
812			return (0);
813	}
814}
815