boot1.c revision 95337
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 95337 2002-04-24 01:40:54Z 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
38/*
39 * This structure will be refined along with the addition of a bootpath
40 * parsing routine when it is necessary to cope with bootpaths that are
41 * not in the exact <devpath>@<controller>,<disk>:<partition> format and
42 * for which we need to evaluate the disklabel ourselves.
43 */
44struct disk {
45	int meta;
46};
47struct disk dsk;
48
49extern uint32_t _end;
50
51static char bname[1024];	/* name of the binary to load */
52static uint32_t fs_off;
53
54int main(void);
55void exit(int);
56static void load(const char *);
57static ino_t lookup(const char *);
58static int xfsread(ino_t, void *, size_t);
59static ssize_t fsread(ino_t, void *, size_t);
60static int dskread(void *, u_int64_t, int);
61static int printf(const char *, ...);
62static int putchar(int);
63
64static void *memcpy(void *, const void *, size_t);
65static void *memset(void *, int, size_t);
66
67/*
68 * Open Firmware interface functions
69 */
70typedef u_int64_t	ofwcell_t;
71typedef int32_t		ofwh_t;
72typedef u_int32_t	u_ofwh_t;
73typedef int (*ofwfp_t)(ofwcell_t []);
74ofwfp_t ofw;			/* the prom Open Firmware entry */
75
76void ofw_init(int, int, int, int, ofwfp_t);
77ofwh_t ofw_finddevice(const char *);
78ofwh_t ofw_open(const char *);
79int ofw_getprop(ofwh_t, const char *, void *, size_t);
80int ofw_read(ofwh_t, void *, size_t);
81int ofw_write(ofwh_t, const void *, size_t);
82int ofw_seek(ofwh_t, u_int64_t);
83
84ofwh_t bootdevh;
85ofwh_t stdinh, stdouth;
86char bootpath[64];
87
88/*
89 * This has to stay here, as the PROM seems to ignore the
90 * entry point specified in the a.out header.  (or elftoaout is broken)
91 */
92
93void
94ofw_init(int d, int d1, int d2, int d3, ofwfp_t ofwaddr)
95{
96	ofwh_t chosenh;
97
98	ofw = ofwaddr;
99
100	chosenh = ofw_finddevice("/chosen");
101	ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh));
102	ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth));
103	ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
104
105	if ((bootdevh = ofw_open(bootpath)) == -1) {
106		printf("Could not open boot device.\n");
107	}
108
109	main();
110	d = d1 = d2 = d3;	/* make GCC happy */
111}
112
113ofwh_t
114ofw_finddevice(const char *name)
115{
116	ofwcell_t args[] = {
117		(ofwcell_t)"finddevice",
118		1,
119		1,
120		(ofwcell_t)name,
121		0
122	};
123
124	if ((*ofw)(args)) {
125		printf("ofw_finddevice: name=\"%s\"\n", name);
126		return (1);
127	}
128	return (args[4]);
129}
130
131int
132ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len)
133{
134	ofwcell_t args[] = {
135		(ofwcell_t)"getprop",
136		4,
137		1,
138		(u_ofwh_t)ofwh,
139		(ofwcell_t)name,
140		(ofwcell_t)buf,
141		len,
142	0
143	};
144
145	if ((*ofw)(args)) {
146		printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n",
147			ofwh, buf, len);
148		return (1);
149	}
150	return (0);
151}
152
153ofwh_t
154ofw_open(const char *path)
155{
156	ofwcell_t args[] = {
157		(ofwcell_t)"open",
158		1,
159		1,
160		(ofwcell_t)path,
161		0
162	};
163
164	if ((*ofw)(args)) {
165		printf("ofw_open: path=\"%s\"\n", path);
166		return (-1);
167	}
168	return (args[4]);
169}
170
171int
172ofw_close(ofwh_t devh)
173{
174	ofwcell_t args[] = {
175		(ofwcell_t)"close",
176		1,
177		0,
178		(u_ofwh_t)devh
179	};
180
181	if ((*ofw)(args)) {
182		printf("ofw_close: devh=0x%x\n", devh);
183		return (1);
184	}
185	return (0);
186}
187
188int
189ofw_read(ofwh_t devh, void *buf, size_t len)
190{
191	ofwcell_t args[] = {
192		(ofwcell_t)"read",
193		4,
194		1,
195		(u_ofwh_t)devh,
196		(ofwcell_t)buf,
197		len,
198		0
199	};
200
201	if ((*ofw)(args)) {
202		printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len);
203		return (1);
204	}
205	return (0);
206}
207
208int
209ofw_write(ofwh_t devh, const void *buf, size_t len)
210{
211	ofwcell_t args[] = {
212		(ofwcell_t)"write",
213		3,
214		1,
215		(u_ofwh_t)devh,
216		(ofwcell_t)buf,
217		len,
218		0
219	};
220
221	if ((*ofw)(args)) {
222		printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len);
223		return (1);
224	}
225	return (0);
226}
227
228int
229ofw_seek(ofwh_t devh, u_int64_t off)
230{
231	ofwcell_t args[] = {
232		(ofwcell_t)"seek",
233		4,
234		1,
235		(u_ofwh_t)devh,
236		off >> 32,
237		off,
238		0
239	};
240
241	if ((*ofw)(args)) {
242		printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off);
243		return (1);
244	}
245	return (0);
246}
247
248static int
249strcmp(const char *s1, const char *s2)
250{
251	for (; *s1 == *s2 && *s1; s1++, s2++)
252		;
253	return ((u_char)*s1 - (u_char)*s2);
254}
255
256static void *
257memset(void *dst, int val, size_t len)
258{
259	void *ret;
260
261	ret = dst;
262	while (len) {
263		*((char *)dst)++ = val;
264		len--;
265	}
266	return (ret);
267}
268
269static int
270fsfind(const char *name, ino_t * ino)
271{
272	char buf[DEV_BSIZE];
273	struct dirent *d;
274	char *s;
275	ssize_t n;
276
277	fs_off = 0;
278	while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0) {
279		for (s = buf; s < buf + DEV_BSIZE;) {
280			d = (void *)s;
281			if (!strcmp(name, d->d_name)) {
282				*ino = d->d_fileno;
283				return (d->d_type);
284			}
285			s += d->d_reclen;
286		}
287	}
288	return (0);
289}
290
291static void
292putc(int c)
293{
294	char d;
295
296	d = c;
297	ofw_write(stdouth, &d, 1);
298}
299
300int
301main(void)
302{
303	if (bname[0] == '\0')
304		memcpy(bname, _PATH_LOADER, sizeof(_PATH_LOADER));
305
306	printf(" \n>> FreeBSD/sparc64 boot block\n"
307	"   Boot path:   %s\n"
308	"   Boot loader: %s\n", bootpath, _PATH_LOADER);
309	load(bname);
310	return (1);
311}
312
313static void
314load(const char *fname)
315{
316	Elf64_Ehdr eh;
317	Elf64_Phdr ep[2];
318	Elf64_Shdr es[2];
319	caddr_t p;
320	ino_t ino;
321	vm_offset_t entry;
322	int i, j;
323
324	if ((ino = lookup(fname)) == 0) {
325		printf("File %s not found\n", fname);
326		return;
327	}
328	if (xfsread(ino, &eh, sizeof(eh)))
329		return;
330	if (!IS_ELF(eh)) {
331		printf("Not an ELF file\n");
332		return;
333	}
334	fs_off = eh.e_phoff;
335	for (j = i = 0; i < eh.e_phnum && j < 2; i++) {
336		if (xfsread(ino, ep + j, sizeof(ep[0])))
337			return;
338		if (ep[j].p_type == PT_LOAD)
339			j++;
340	}
341	for (i = 0; i < j; i++) {
342		p = (caddr_t)ep[i].p_vaddr;
343		fs_off = ep[i].p_offset;
344		if (xfsread(ino, p, ep[i].p_filesz))
345			return;
346		/*
347		 * Assume the second program header table entry
348		 * to contain data and bss.  Clear out the .bss section.
349		 */
350		if (i == 1) {
351			memset(p + ep[i].p_filesz, 0,
352			    ep[i].p_memsz - ep[i].p_filesz);
353		}
354	}
355	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
356	if (eh.e_shnum == eh.e_shstrndx + 3) {
357		fs_off = eh.e_shoff + sizeof(es[0]) * (eh.e_shstrndx + 1);
358		if (xfsread(ino, &es, sizeof(es)))
359			return;
360		for (i = 0; i < 2; i++) {
361			memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
362			p += sizeof(es[i].sh_size);
363			fs_off = es[i].sh_offset;
364			if (xfsread(ino, p, es[i].sh_size))
365				return;
366			p += es[i].sh_size;
367		}
368	}
369	entry = eh.e_entry;
370	ofw_close(bootdevh);
371	(*(void (*)(int, int, int, int, ofwfp_t))entry)(0, 0, 0, 0, ofw);
372}
373
374static ino_t
375lookup(const char *path)
376{
377	char name[MAXNAMLEN + 1];
378	const char *s;
379	ino_t ino;
380	ssize_t n;
381	int dt;
382
383	ino = ROOTINO;
384	dt = DT_DIR;
385	name[0] = '/';
386	name[1] = '\0';
387	for (;;) {
388		if (*path == '/')
389			path++;
390		if (!*path)
391			break;
392		for (s = path; *s && *s != '/'; s++)
393			;
394		if ((n = s - path) > MAXNAMLEN)
395			return (0);
396		memcpy(name, path, n);
397		name[n] = 0;
398		if (dt != DT_DIR) {
399			printf("%s: not a directory.\n", name);
400			return (0);
401		}
402		if ((dt = fsfind(name, &ino)) <= 0)
403			break;
404		path = s;
405	}
406	return (dt == DT_REG ? ino : 0);
407}
408
409static int
410xfsread(ino_t inode, void *buf, size_t nbyte)
411{
412	if (fsread(inode, buf, nbyte) != (ssize_t)nbyte) {
413		printf("Invalid %s\n", "format");
414		return (-1);
415	}
416	return (0);
417}
418
419static ssize_t
420fsread(ino_t inode, void *buf, size_t nbyte)
421{
422	static struct fs fs;
423	static struct dinode din;
424	static char blkbuf[BSIZEMAX];
425	static ufs_daddr_t indbuf[BSIZEMAX / sizeof(ufs_daddr_t)];
426	static ino_t inomap;
427	static ufs_daddr_t blkmap, indmap;
428	static unsigned int fsblks;
429	char *s;
430	ufs_daddr_t lbn, addr;
431	size_t n, nb, off;
432
433	if (!dsk.meta) {
434		inomap = 0;
435		if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE))
436			return (-1);
437		memcpy(&fs, blkbuf, sizeof(fs));
438		if (fs.fs_magic != FS_MAGIC) {
439			printf("Not ufs\n");
440			return (-1);
441		}
442		fsblks = fs.fs_bsize >> DEV_BSHIFT;
443		dsk.meta++;
444	}
445	if (!inode)
446		return (0);
447	if (inomap != inode) {
448		if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)),
449		    fsblks))
450			return (-1);
451		din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)];
452		inomap = inode;
453		fs_off = 0;
454		blkmap = indmap = 0;
455	}
456	s = buf;
457	if (nbyte > (n = din.di_size - fs_off))
458		nbyte = n;
459	nb = nbyte;
460	while (nb) {
461		lbn = lblkno(&fs, fs_off);
462		if (lbn < NDADDR)
463			addr = din.di_db[lbn];
464		else {
465			if (indmap != din.di_ib[0]) {
466				if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]),
467				    fsblks))
468					return (-1);
469				indmap = din.di_ib[0];
470			}
471			addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)];
472		}
473		n = dblksize(&fs, &din, lbn);
474		if (blkmap != addr) {
475			if (dskread(blkbuf, fsbtodb(&fs, addr),
476			    n >> DEV_BSHIFT)) {
477				return (-1);
478			}
479			blkmap = addr;
480		}
481		off = blkoff(&fs, fs_off);
482		n -= off;
483		if (n > nb)
484			n = nb;
485		memcpy(s, blkbuf + off, n);
486		s += n;
487		fs_off += n;
488		nb -= n;
489	}
490	return (nbyte);
491}
492
493static int
494dskread(void *buf, u_int64_t lba, int nblk)
495{
496	/*
497	 * The OpenFirmware should open the correct partition for us.
498	 * That means, if we read from offset zero on an open instance handle,
499	 * we should read from offset zero of that partition.
500	 */
501	ofw_seek(bootdevh, lba * DEV_BSIZE);
502	ofw_read(bootdevh, buf, nblk * DEV_BSIZE);
503	return (0);
504}
505
506static int
507printf(const char *fmt,...)
508{
509	static const char digits[16] = "0123456789abcdef";
510	va_list ap;
511	char buf[10];
512	char *s;
513	unsigned long int r, u;
514	int c, longp;
515
516	va_start(ap, fmt);
517	longp = 0;
518	while ((c = *fmt++)) {
519		if (c == '%' || longp) {
520			if (c == '%')
521				c = *fmt++;
522			switch (c) {
523			case 'c':
524				if (longp)
525					break;
526				putchar(va_arg(ap, int));
527				continue;
528			case 's':
529				if (longp)
530					break;
531				for (s = va_arg(ap, char *); *s; s++)
532					putchar(*s);
533				continue;
534			case 'p':
535				if (longp)
536					break;
537				if (c == 'p') {
538					putchar('0');
539					putchar('x');
540				}
541			case 'u':
542			case 'x':
543				r = c == 'u' ? 10U : 16U;
544				u = (c == 'p' || longp) ?
545				    va_arg(ap, unsigned long) :
546				    va_arg(ap, unsigned int);
547				s = buf;
548				do
549					*s++ = digits[u % r];
550				while (u /= r);
551				while (--s >= buf)
552					putchar(*s);
553				longp = 0;
554				continue;
555			case 'l':
556				if (longp)
557					break;
558				longp = 1;
559				continue;
560			}
561			longp = 0;
562		}
563		putchar(c);
564	}
565	va_end(ap);
566	return (0);
567}
568
569static int
570putchar(int c)
571{
572	if (c == '\n')
573		putc('\r');
574	putc(c);
575	return (c);
576}
577
578static void *
579memcpy(void *dst, const void *src, size_t size)
580{
581	const char *s;
582	char *d;
583
584	for (d = dst, s = src; size; size--)
585		*d++ = *s++;
586	return (dst);
587}
588