boot1.c revision 95351
1/*
2 * Copyright (c) 1998 Robert Nordier
3 * All rights reserved.
4 * Copyright (c) 2001 Robert Drehmel
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms are freely
8 * permitted provided that the above copyright notice and this
9 * paragraph and the following disclaimer are duplicated in all
10 * such forms.
11 *
12 * This software is provided "AS IS" and without any express or
13 * implied warranties, including, without limitation, the implied
14 * warranties of merchantability and fitness for a particular
15 * purpose.
16 *
17 */
18
19#include <sys/cdefs.h>
20__FBSDID("$FreeBSD: head/sys/boot/sparc64/boot1/boot1.c 95351 2002-04-24 05:54:10Z jake $");
21
22#include <sys/param.h>
23#include <sys/reboot.h>
24#include <sys/diskslice.h>
25#include <sys/disklabel.h>
26#include <sys/dirent.h>
27#include <machine/elf.h>
28#include <machine/stdarg.h>
29
30#include <ufs/ffs/fs.h>
31#include <ufs/ufs/dinode.h>
32
33#define _PATH_LOADER	"/boot/loader"
34#define _PATH_KERNEL	"/boot/kernel/kernel"
35
36#define BSIZEMAX	8192
37
38typedef int putc_func_t(int c, void *arg);
39typedef int32_t ofwh_t;
40
41struct sp_data {
42	char	*sp_buf;
43	u_int	sp_len;
44	u_int	sp_size;
45};
46
47static const char digits[] = "0123456789abcdef";
48
49static char bootpath[128];
50static char bootargs[128];
51
52static ofwh_t bootdev;
53
54static struct fs fs;
55static ino_t inomap;
56static char blkbuf[BSIZEMAX];
57static unsigned int fsblks;
58
59static uint32_t fs_off;
60
61int main(int ac, char **av);
62
63static void exit(int) __dead2;
64static void load(const char *);
65static ino_t lookup(const char *);
66static ssize_t fsread(ino_t, void *, size_t);
67static int dskread(void *, u_int64_t, int);
68
69static void usage(void);
70
71static void bcopy(const void *src, void *dst, size_t len);
72static void bzero(void *b, size_t len);
73
74static int mount(const char *device);
75
76static void panic(const char *fmt, ...) __dead2;
77static int printf(const char *fmt, ...);
78static int putchar(int c, void *arg);
79static int vprintf(const char *fmt, va_list ap);
80static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap);
81
82static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap);
83static int __putc(int c, void *arg);
84static int __puts(const char *s, putc_func_t *putc, void *arg);
85static int __sputc(int c, void *arg);
86static char *__uitoa(char *buf, u_int val, int base);
87static char *__ultoa(char *buf, u_long val, int base);
88
89/*
90 * Open Firmware interface functions
91 */
92typedef u_int64_t	ofwcell_t;
93typedef u_int32_t	u_ofwh_t;
94typedef int (*ofwfp_t)(ofwcell_t []);
95ofwfp_t ofw;			/* the prom Open Firmware entry */
96
97void ofw_init(int, int, int, int, ofwfp_t);
98ofwh_t ofw_finddevice(const char *);
99ofwh_t ofw_open(const char *);
100int ofw_getprop(ofwh_t, const char *, void *, size_t);
101int ofw_read(ofwh_t, void *, size_t);
102int ofw_write(ofwh_t, const void *, size_t);
103int ofw_seek(ofwh_t, u_int64_t);
104void ofw_exit(void) __dead2;
105
106ofwh_t bootdevh;
107ofwh_t stdinh, stdouth;
108
109/*
110 * This has to stay here, as the PROM seems to ignore the
111 * entry point specified in the a.out header.  (or elftoaout is broken)
112 */
113
114void
115ofw_init(int d, int d1, int d2, int d3, ofwfp_t ofwaddr)
116{
117	ofwh_t chosenh;
118	char *av[16];
119	char *p;
120	int ac;
121
122	ofw = ofwaddr;
123
124	chosenh = ofw_finddevice("/chosen");
125	ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh));
126	ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth));
127	ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs));
128	ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
129
130	bootargs[sizeof(bootargs) - 1] = '\0';
131	bootpath[sizeof(bootpath) - 1] = '\0';
132
133	ac = 0;
134	p = bootargs;
135	for (;;) {
136		while (*p == ' ' && *p != '\0')
137			p++;
138		if (*p == '\0' || ac >= 16)
139			break;
140		av[ac++] = p;
141		while (*p != ' ' && *p != '\0')
142			p++;
143		if (*p != '\0')
144			*p++ = '\0';
145	}
146
147	exit(main(ac, av));
148}
149
150ofwh_t
151ofw_finddevice(const char *name)
152{
153	ofwcell_t args[] = {
154		(ofwcell_t)"finddevice",
155		1,
156		1,
157		(ofwcell_t)name,
158		0
159	};
160
161	if ((*ofw)(args)) {
162		printf("ofw_finddevice: name=\"%s\"\n", name);
163		return (1);
164	}
165	return (args[4]);
166}
167
168int
169ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
170{
171	ofwcell_t args[] = {
172		(ofwcell_t)"getprop",
173		4,
174		1,
175		(u_ofwh_t)ofwh,
176		(ofwcell_t)name,
177		(ofwcell_t)buf,
178		len,
179	0
180	};
181
182	if ((*ofw)(args)) {
183		printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n",
184			ofwh, buf, len);
185		return (1);
186	}
187	return (0);
188}
189
190ofwh_t
191ofw_open(const char *path)
192{
193	ofwcell_t args[] = {
194		(ofwcell_t)"open",
195		1,
196		1,
197		(ofwcell_t)path,
198		0
199	};
200
201	if ((*ofw)(args)) {
202		printf("ofw_open: path=\"%s\"\n", path);
203		return (-1);
204	}
205	return (args[4]);
206}
207
208int
209ofw_close(ofwh_t devh)
210{
211	ofwcell_t args[] = {
212		(ofwcell_t)"close",
213		1,
214		0,
215		(u_ofwh_t)devh
216	};
217
218	if ((*ofw)(args)) {
219		printf("ofw_close: devh=0x%x\n", devh);
220		return (1);
221	}
222	return (0);
223}
224
225int
226ofw_read(ofwh_t devh, void *buf, size_t len)
227{
228	ofwcell_t args[] = {
229		(ofwcell_t)"read",
230		4,
231		1,
232		(u_ofwh_t)devh,
233		(ofwcell_t)buf,
234		len,
235		0
236	};
237
238	if ((*ofw)(args)) {
239		printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len);
240		return (1);
241	}
242	return (0);
243}
244
245int
246ofw_write(ofwh_t devh, const void *buf, size_t len)
247{
248	ofwcell_t args[] = {
249		(ofwcell_t)"write",
250		3,
251		1,
252		(u_ofwh_t)devh,
253		(ofwcell_t)buf,
254		len,
255		0
256	};
257
258	if ((*ofw)(args)) {
259		printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len);
260		return (1);
261	}
262	return (0);
263}
264
265int
266ofw_seek(ofwh_t devh, u_int64_t off)
267{
268	ofwcell_t args[] = {
269		(ofwcell_t)"seek",
270		4,
271		1,
272		(u_ofwh_t)devh,
273		off >> 32,
274		off,
275		0
276	};
277
278	if ((*ofw)(args)) {
279		printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off);
280		return (1);
281	}
282	return (0);
283}
284
285void
286ofw_exit(void)
287{
288	ofwcell_t args[3];
289
290	args[0] = (ofwcell_t)"exit";
291	args[1] = 0;
292	args[2] = 0;
293
294	for (;;)
295		(*ofw)(args);
296}
297
298static void
299bcopy(const void *dst, void *src, size_t len)
300{
301	const char *d = dst;
302	char *s = src;
303
304	while (len-- != 0)
305		*s++ = *d++;
306}
307
308static void
309bzero(void *b, size_t len)
310{
311	char *p = b;
312
313	while (len-- != 0)
314		*p++ = 0;
315}
316
317static int
318strcmp(const char *s1, const char *s2)
319{
320	for (; *s1 == *s2 && *s1; s1++, s2++)
321		;
322	return ((u_char)*s1 - (u_char)*s2);
323}
324
325static int
326fsfind(const char *name, ino_t * ino)
327{
328	char buf[DEV_BSIZE];
329	struct dirent *d;
330	char *s;
331	ssize_t n;
332
333	fs_off = 0;
334	while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) {
335		for (s = buf; s < buf + DEV_BSIZE;) {
336			d = (void *)s;
337			if (!strcmp(name, d->d_name)) {
338				*ino = d->d_fileno;
339				return (d->d_type);
340			}
341			s += d->d_reclen;
342		}
343	}
344	return (0);
345}
346
347int
348main(int ac, char **av)
349{
350	const char *path;
351	int i;
352
353	path = _PATH_LOADER;
354	for (i = 0; i < ac; i++) {
355		switch (av[i][0]) {
356		case '-':
357			switch (av[i][1]) {
358			default:
359				usage();
360			}
361			break;
362		default:
363			path = av[i];
364			break;
365		}
366	}
367
368	printf(" \n>> FreeBSD/sparc64 boot block\n"
369	"   Boot path:   %s\n"
370	"   Boot loader: %s\n", bootpath, path);
371
372	if (mount(bootpath) == -1)
373		panic("mount");
374
375	load(path);
376	return (1);
377}
378
379static void
380usage(void)
381{
382
383	printf("usage: boot device [/path/to/loader]\n");
384	exit(1);
385}
386
387static void
388exit(int code)
389{
390
391	ofw_exit();
392}
393
394static int
395mount(const char *device)
396{
397
398	if ((bootdev = ofw_open(device)) == -1) {
399		printf("mount: can't open device\n");
400		return (-1);
401	}
402	if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE)) {
403		printf("mount: can't read superblock\n");
404		return (-1);
405	}
406	inomap = 0;
407	bcopy(blkbuf, &fs, sizeof(fs));
408	if (fs.fs_magic != FS_MAGIC) {
409		printf("mount: not ufs\n");
410		return (-1);
411	}
412	fsblks = fs.fs_bsize >> DEV_BSHIFT;
413	return (0);
414}
415
416static void
417load(const char *fname)
418{
419	Elf64_Ehdr eh;
420	Elf64_Phdr ph;
421	caddr_t p;
422	ino_t ino;
423	int i;
424
425	if ((ino = lookup(fname)) == 0) {
426		printf("File %s not found\n", fname);
427		return;
428	}
429	if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) {
430		printf("Can't read elf header\n");
431		return;
432	}
433	if (!IS_ELF(eh)) {
434		printf("Not an ELF file\n");
435		return;
436	}
437	for (i = 0; i < eh.e_phnum; i++) {
438		fs_off = eh.e_phoff + i * eh.e_phentsize;
439		if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) {
440			printf("Can't read program header %d\n", i);
441			return;
442		}
443		if (ph.p_type != PT_LOAD)
444			continue;
445		fs_off = ph.p_offset;
446		p = (caddr_t)ph.p_vaddr;
447		if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) {
448			printf("Can't read content of section %d\n", i);
449			return;
450		}
451		if (ph.p_filesz != ph.p_memsz)
452			bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz);
453	}
454	ofw_close(bootdev);
455	(*(void (*)(int, int, int, int, ofwfp_t))eh.e_entry)(0, 0, 0, 0, ofw);
456}
457
458static ino_t
459lookup(const char *path)
460{
461	char name[MAXNAMLEN + 1];
462	const char *s;
463	ino_t ino;
464	ssize_t n;
465	int dt;
466
467	ino = ROOTINO;
468	dt = DT_DIR;
469	name[0] = '/';
470	name[1] = '\0';
471	for (;;) {
472		if (*path == '/')
473			path++;
474		if (!*path)
475			break;
476		for (s = path; *s && *s != '/'; s++)
477			;
478		if ((n = s - path) > MAXNAMLEN)
479			return (0);
480		bcopy(path, name, n);
481		name[n] = 0;
482		if (dt != DT_DIR) {
483			printf("%s: not a directory.\n", name);
484			return (0);
485		}
486		if ((dt = fsfind(name, &ino)) <= 0)
487			break;
488		path = s;
489	}
490	return (dt == DT_REG ? ino : 0);
491}
492
493static ssize_t
494fsread(ino_t inode, void *buf, size_t nbyte)
495{
496	static struct dinode din;
497	static ufs_daddr_t indbuf[BSIZEMAX / sizeof(ufs_daddr_t)];
498	static ufs_daddr_t blkmap, indmap;
499	char *s;
500	ufs_daddr_t lbn, addr;
501	size_t n, nb, off;
502
503	if (!inode)
504		return (0);
505	if (inomap != inode) {
506		if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)),
507		    fsblks))
508			return (-1);
509		bcopy(blkbuf + ((inode % INOPB(&fs)) * sizeof(din)), &din,
510		    sizeof(din));
511		inomap = inode;
512		fs_off = 0;
513		blkmap = indmap = 0;
514	}
515	s = buf;
516	if (nbyte > (n = din.di_size - fs_off))
517		nbyte = n;
518	nb = nbyte;
519	while (nb) {
520		lbn = lblkno(&fs, fs_off);
521		if (lbn < NDADDR)
522			addr = din.di_db[lbn];
523		else {
524			if (indmap != din.di_ib[0]) {
525				if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]),
526				    fsblks))
527					return (-1);
528				indmap = din.di_ib[0];
529			}
530			addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)];
531		}
532		n = dblksize(&fs, &din, lbn);
533		if (blkmap != addr) {
534			if (dskread(blkbuf, fsbtodb(&fs, addr),
535			    n >> DEV_BSHIFT)) {
536				return (-1);
537			}
538			blkmap = addr;
539		}
540		off = blkoff(&fs, fs_off);
541		n -= off;
542		if (n > nb)
543			n = nb;
544		bcopy(blkbuf + off, s, n);
545		s += n;
546		fs_off += n;
547		nb -= n;
548	}
549	return (nbyte);
550}
551
552static int
553dskread(void *buf, u_int64_t lba, int nblk)
554{
555	/*
556	 * The OpenFirmware should open the correct partition for us.
557	 * That means, if we read from offset zero on an open instance handle,
558	 * we should read from offset zero of that partition.
559	 */
560	ofw_seek(bootdev, lba * DEV_BSIZE);
561	ofw_read(bootdev, buf, nblk * DEV_BSIZE);
562	return (0);
563}
564
565static void
566panic(const char *fmt, ...)
567{
568	char buf[128];
569	va_list ap;
570
571	va_start(ap, fmt);
572	vsnprintf(buf, sizeof buf, fmt, ap);
573	printf("panic: %s\n", buf);
574	va_end(ap);
575
576	exit(1);
577}
578
579static int
580printf(const char *fmt, ...)
581{
582	va_list ap;
583	int ret;
584
585	va_start(ap, fmt);
586	ret = vprintf(fmt, ap);
587	va_end(ap);
588	return (ret);
589}
590
591static int
592putchar(int c, void *arg)
593{
594	char buf;
595
596	if (c == '\n') {
597		buf = '\r';
598		ofw_write(stdouth, &buf, 1);
599	}
600	buf = c;
601	ofw_write(stdouth, &buf, 1);
602	return (1);
603}
604
605static int
606vprintf(const char *fmt, va_list ap)
607{
608	int ret;
609
610	ret = __printf(fmt, putchar, 0, ap);
611	return (ret);
612}
613
614static int
615vsnprintf(char *str, size_t sz, const char *fmt, va_list ap)
616{
617	struct sp_data sp;
618	int ret;
619
620	sp.sp_buf = str;
621	sp.sp_len = 0;
622	sp.sp_size = sz;
623	ret = __printf(fmt, __sputc, &sp, ap);
624	return (ret);
625}
626
627static int
628__printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap)
629{
630	char buf[(sizeof(long) * 8) + 1];
631	char *nbuf;
632	u_long ul;
633	u_int ui;
634	int lflag;
635	int sflag;
636	char *s;
637	int pad;
638	int ret;
639	int c;
640
641	nbuf = &buf[sizeof buf - 1];
642	ret = 0;
643	while ((c = *fmt++) != 0) {
644		if (c != '%') {
645			ret += putc(c, arg);
646			continue;
647		}
648		lflag = 0;
649		sflag = 0;
650		pad = 0;
651reswitch:	c = *fmt++;
652		switch (c) {
653		case '#':
654			sflag = 1;
655			goto reswitch;
656		case '%':
657			ret += putc('%', arg);
658			break;
659		case 'c':
660			c = va_arg(ap, int);
661			ret += putc(c, arg);
662			break;
663		case 'd':
664			if (lflag == 0) {
665				ui = (u_int)va_arg(ap, int);
666				if (ui < (int)ui) {
667					ui = -ui;
668					ret += putc('-', arg);
669				}
670				s = __uitoa(nbuf, ui, 10);
671			} else {
672				ul = (u_long)va_arg(ap, long);
673				if (ul < (long)ul) {
674					ul = -ul;
675					ret += putc('-', arg);
676				}
677				s = __ultoa(nbuf, ul, 10);
678			}
679			ret += __puts(s, putc, arg);
680			break;
681		case 'l':
682			lflag = 1;
683			goto reswitch;
684		case 'o':
685			if (lflag == 0) {
686				ui = (u_int)va_arg(ap, u_int);
687				s = __uitoa(nbuf, ui, 8);
688			} else {
689				ul = (u_long)va_arg(ap, u_long);
690				s = __ultoa(nbuf, ul, 8);
691			}
692			ret += __puts(s, putc, arg);
693			break;
694		case 'p':
695			ul = (u_long)va_arg(ap, void *);
696			s = __ultoa(nbuf, ul, 16);
697			ret += __puts("0x", putc, arg);
698			ret += __puts(s, putc, arg);
699			break;
700		case 's':
701			s = va_arg(ap, char *);
702			ret += __puts(s, putc, arg);
703			break;
704		case 'u':
705			if (lflag == 0) {
706				ui = va_arg(ap, u_int);
707				s = __uitoa(nbuf, ui, 10);
708			} else {
709				ul = va_arg(ap, u_long);
710				s = __ultoa(nbuf, ul, 10);
711			}
712			ret += __puts(s, putc, arg);
713			break;
714		case 'x':
715			if (lflag == 0) {
716				ui = va_arg(ap, u_int);
717				s = __uitoa(nbuf, ui, 16);
718			} else {
719				ul = va_arg(ap, u_long);
720				s = __ultoa(nbuf, ul, 16);
721			}
722			if (sflag)
723				ret += __puts("0x", putc, arg);
724			ret += __puts(s, putc, arg);
725			break;
726		case '0': case '1': case '2': case '3': case '4':
727		case '5': case '6': case '7': case '8': case '9':
728			pad = pad * 10 + c - '0';
729			goto reswitch;
730		default:
731			break;
732		}
733	}
734	return (ret);
735}
736
737static int
738__sputc(int c, void *arg)
739{
740	struct sp_data *sp;
741
742	sp = arg;
743	if (sp->sp_len < sp->sp_size)
744		sp->sp_buf[sp->sp_len++] = c;
745	sp->sp_buf[sp->sp_len] = '\0';
746	return (1);
747}
748
749static int
750__puts(const char *s, putc_func_t *putc, void *arg)
751{
752	const char *p;
753	int ret;
754
755	ret = 0;
756	for (p = s; *p != '\0'; p++)
757		ret += putc(*p, arg);
758	return (ret);
759}
760
761static char *
762__uitoa(char *buf, u_int ui, int base)
763{
764	char *p;
765
766	p = buf;
767	*p = '\0';
768	do
769		*--p = digits[ui % base];
770	while ((ui /= base) != 0);
771	return (p);
772}
773
774static char *
775__ultoa(char *buf, u_long ul, int base)
776{
777	char *p;
778
779	p = buf;
780	*p = '\0';
781	do
782		*--p = digits[ul % base];
783	while ((ul /= base) != 0);
784	return (p);
785}
786