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