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