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