1 /**	$MirOS: src/sys/arch/sparc/stand/installboot/installboot.c,v 1.6 2007/10/16 20:29:33 tg Exp $ */
2 /*	$OpenBSD: installboot.c,v 1.4 2003/08/25 23:36:46 tedu Exp $	*/
3 /*	$NetBSD: installboot.c,v 1.1 1997/06/01 03:39:45 mrg Exp $	*/
4 
5 /*
6  * Copyright (c) 1994 Paul Kranenburg
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Paul Kranenburg.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/time.h>
38 #include <sys/stat.h>
39 #include <sys/sysctl.h>
40 #include <ufs/ufs/dinode.h>
41 #include <ufs/ufs/dir.h>
42 #include <ufs/ffs/fs.h>
43 #include <err.h>
44 #include <a.out.h>
45 #include <fcntl.h>
46 #include <nlist.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 __RCSID("$MirOS: src/sys/arch/sparc/stand/installboot/installboot.c,v 1.6 2007/10/16 20:29:33 tg Exp $");
53 
54 extern const char *__progname;
55 
56 int	verbose, nowrite, hflag;
57 char	*boot, *proto, *dev;
58 
59 struct nlist nl[] = {
60 #define X_BLOCKTABLE	0
61 	{{"_block_table"}},
62 #define X_BLOCKCOUNT	1
63 	{{"_block_count"}},
64 #define X_BLOCKSIZE	2
65 	{{"_block_size"}},
66 	{{NULL}}
67 };
68 int32_t	*block_table;		/* block number array in prototype image */
69 int32_t	*block_count_p;		/* size of this array */
70 int32_t	*block_size_p;		/* filesystem block size */
71 int32_t	max_block_count;
72 
73 char	*karch;
74 char	cpumodel[130];
75 
76 int	isofsblk = 0;
77 int	isofseblk = 0;
78 
79 char		*loadprotoblocks(char *, long *);
80 int		loadblocknums(char *, int);
81 static void	devread(int, void *, daddr_t, size_t, char *);
82 __dead static void usage(void);
83 int 		main(int, char *[]);
84 
85 static void
usage()86 usage()
87 {
88 	fprintf(stderr,
89 	    "usage:\t%s [-nvh] [-a <karch>] <boot> <proto> <device>\n"
90 	    "\t%s [-nvh] [-a <karch>] -s isofsblk -e isofseblk\n"
91 	    "\t    <boot (ignored, can be /dev/null)> <proto> <device>\n",
92 	    __progname, __progname);
93 	exit(1);
94 }
95 
96 int
main(argc,argv)97 main(argc, argv)
98 	int argc;
99 	char *argv[];
100 {
101 	int	c;
102 	int	devfd;
103 	char	*protostore;
104 	long	protosize;
105 	int	mib[2];
106 	size_t	size;
107 
108 	while ((c = getopt(argc, argv, "a:vnhs:e:")) != -1) {
109 		switch (c) {
110 		case 'a':
111 			karch = optarg;
112 			break;
113 		case 'h':	/* Note: for backwards compatibility */
114 			/* Don't strip a.out header */
115 			hflag = 1;
116 			break;
117 		case 'n':
118 			/* Do not actually write the bootblock to disk */
119 			nowrite = 1;
120 			break;
121 		case 'v':
122 			/* Chat */
123 			verbose = 1;
124 			break;
125 		case 's':
126 			isofsblk = atoi(optarg);
127 			break;
128 		case 'e':
129 			isofseblk = atoi(optarg);
130 			break;
131 		default:
132 			usage();
133 		}
134 	}
135 
136 	if (argc - optind < 3) {
137 		usage();
138 	}
139 
140 	boot = argv[optind];
141 	proto = argv[optind + 1];
142 	dev = argv[optind + 2];
143 
144 	if (karch == NULL) {
145 		mib[0] = CTL_HW;
146 		mib[1] = HW_MODEL;
147 		size = sizeof(cpumodel);
148 		if (sysctl(mib, 2, cpumodel, &size, NULL, 0) == -1)
149 			err(1, "sysctl");
150 
151 		if (size < 5 || strncmp(cpumodel, "SUN-4", 5) != 0) /*XXX*/
152 			/* Assume a sun4c/sun4m */
153 			karch = "sun4c";
154 		else
155 			karch = "sun4";
156 	}
157 
158 	if (verbose) {
159 		printf("boot: %s\n", boot);
160 		printf("proto: %s\n", proto);
161 		printf("device: %s\n", dev);
162 		printf("architecture: %s\n", karch);
163 	}
164 
165 	if (strcmp(karch, "sun4") == 0) {
166 		hflag = 0;
167 	} else if (strcmp(karch, "sun4c") == 0) {
168 		hflag = 1;
169 	} else if (strcmp(karch, "sun4m") == 0) {
170 		hflag = 1;
171 	} else
172 		errx(1, "Unsupported architecture");
173 
174 	/* Load proto blocks into core */
175 	if ((protostore = loadprotoblocks(proto, &protosize)) == NULL)
176 		exit(1);
177 
178 	/* Open and check raw disk device */
179 	if ((devfd = open(dev, O_RDONLY, 0)) < 0)
180 		err(1, "open: %s", dev);
181 
182 	/* Extract and load block numbers */
183 	if (loadblocknums(boot, devfd) != 0)
184 		exit(1);
185 
186 	(void)close(devfd);
187 
188 	if (nowrite)
189 		return 0;
190 
191 	/* Write patched proto bootblocks into the superblock */
192 	if (protosize > SBSIZE - DEV_BSIZE)
193 		errx(1, "proto bootblocks too big");
194 
195 	if ((devfd = open(dev, O_RDWR, 0)) < 0)
196 		err(1, "open: %s", dev);
197 
198 	if (lseek(devfd, DEV_BSIZE, SEEK_SET) != DEV_BSIZE)
199 		err(1, "lseek bootstrap");
200 
201 	/* Sync filesystems (to clean in-memory superblock?) */
202 	sync();
203 
204 	if (write(devfd, protostore, protosize) != protosize)
205 		err(1, "write bootstrap");
206 	(void)close(devfd);
207 	return 0;
208 }
209 
210 char *
loadprotoblocks(fname,size)211 loadprotoblocks(fname, size)
212 	char *fname;
213 	long *size;
214 {
215 	int	fd, sz;
216 	char	*bp;
217 	struct	stat statbuf;
218 	struct	exec *hp;
219 	long	off;
220 
221 	/* Locate block number array in proto file */
222 	if (nlist(fname, nl) != 0) {
223 		warnx("nlist: %s: symbols not found", fname);
224 		return NULL;
225 	}
226 	if (nl[X_BLOCKTABLE].n_type != N_DATA + N_EXT) {
227 		warnx("nlist: %s: wrong type (should be %04X, is %04X)",
228 		    nl[X_BLOCKTABLE].n_un.n_name, N_DATA + N_EXT,
229 		    nl[X_BLOCKTABLE].n_type);
230 		return NULL;
231 	}
232 	if (nl[X_BLOCKCOUNT].n_type != N_DATA + N_EXT) {
233 		warnx("nlist: %s: wrong type (should be %04X, is %04X)",
234 		    nl[X_BLOCKCOUNT].n_un.n_name, N_DATA + N_EXT,
235 		    nl[X_BLOCKCOUNT].n_type);
236 		return NULL;
237 	}
238 	if (nl[X_BLOCKSIZE].n_type != N_DATA + N_EXT) {
239 		warnx("nlist: %s: wrong type (should be %04X, is %04X)",
240 		    nl[X_BLOCKSIZE].n_un.n_name, N_DATA + N_EXT,
241 		    nl[X_BLOCKSIZE].n_type);
242 		return NULL;
243 	}
244 
245 	if ((fd = open(fname, O_RDONLY)) < 0) {
246 		warn("open: %s", fname);
247 		return NULL;
248 	}
249 	if (fstat(fd, &statbuf) != 0) {
250 		warn("fstat: %s", fname);
251 		close(fd);
252 		return NULL;
253 	}
254 	if ((bp = calloc(roundup(statbuf.st_size, DEV_BSIZE), 1)) == NULL) {
255 		warnx("malloc: %s: no memory", fname);
256 		close(fd);
257 		return NULL;
258 	}
259 	if (read(fd, bp, statbuf.st_size) != statbuf.st_size) {
260 		warn("read: %s", fname);
261 		free(bp);
262 		close(fd);
263 		return NULL;
264 	}
265 	close(fd);
266 
267 	hp = (struct exec *)bp;
268 	sz = (hflag ? sizeof(*hp) : 0) + hp->a_text + hp->a_data;
269 	sz = roundup(sz, DEV_BSIZE);
270 
271 	/* Calculate the symbols' location within the proto file */
272 	off = N_DATOFF(*hp) - N_DATADDR(*hp) - (hp->a_entry - N_TXTADDR(*hp));
273 	block_table = (daddr_t *) (bp + nl[X_BLOCKTABLE].n_value + off);
274 	block_count_p = (int32_t *)(bp + nl[X_BLOCKCOUNT].n_value + off);
275 	block_size_p = (int32_t *) (bp + nl[X_BLOCKSIZE].n_value + off);
276 	if ((int)block_table & 3) {
277 		warn("%s: invalid address: block_table = %p",
278 		     fname, block_table);
279 		free(bp);
280 		close(fd);
281 		return NULL;
282 	}
283 	if ((int)block_count_p & 3) {
284 		warn("%s: invalid address: block_count_p = %p",
285 		     fname, block_count_p);
286 		free(bp);
287 		close(fd);
288 		return NULL;
289 	}
290 	if ((int)block_size_p & 3) {
291 		warn("%s: invalid address: block_size_p = %p",
292 		     fname, block_size_p);
293 		free(bp);
294 		close(fd);
295 		return NULL;
296 	}
297 	max_block_count = *block_count_p;
298 
299 	if (verbose) {
300 		printf("%s: entry point %#x\n", fname, hp->a_entry);
301 		printf("%s: a.out header %s\n", fname,
302 			hflag?"left on":"stripped off");
303 		printf("proto bootblock size %ld\n", sz);
304 		printf("room for %d filesystem blocks at %#x\n",
305 			max_block_count, nl[X_BLOCKTABLE].n_value);
306 	}
307 
308 	/*
309 	 * We convert the a.out header in-vitro into something that
310 	 * Sun PROMs understand.
311 	 * Old-style (sun4) ROMs do not expect a header at all, so
312 	 * we turn the first two words into code that gets us past
313 	 * the 32-byte header where the actual code begins. In assembly
314 	 * speak:
315 	 *	.word	MAGIC		! a NOP
316 	 *	ba,a	start		!
317 	 *	.skip	24		! pad
318 	 * start:
319 	 */
320 
321 #define SUN_MAGIC	0x01030107
322 #define SUN4_BASTART	0x30800007	/* i.e.: ba,a `start' */
323 	*((int *)bp) = SUN_MAGIC;
324 	*((int *)bp + 1) = SUN4_BASTART;
325 
326 	*size = sz;
327 	return (hflag ? bp : (bp + sizeof(struct exec)));
328 }
329 
330 static void
devread(fd,buf,blk,size,msg)331 devread(fd, buf, blk, size, msg)
332 	int	fd;
333 	void	*buf;
334 	daddr_t	blk;
335 	size_t	size;
336 	char	*msg;
337 {
338 	if (lseek(fd, dbtob(blk), SEEK_SET) != dbtob(blk))
339 		err(1, "%s: devread: lseek", msg);
340 
341 	if (read(fd, buf, size) != size)
342 		err(1, "%s: devread: read", msg);
343 }
344 
345 static char sblock[SBSIZE];
346 
347 int
loadblocknums(boot,devfd)348 loadblocknums(boot, devfd)
349 char	*boot;
350 int	devfd;
351 {
352 	int		i, fd;
353 	struct	stat	statbuf;
354 	struct	statfs	statfsbuf;
355 	struct fs	*fs;
356 	char		*buf;
357 	daddr_t		blk, *ap;
358 	struct ufs1_dinode	*ip;
359 	int		ndb;
360 
361 	if (isofsblk) {
362 		*block_size_p = 512;
363 		*block_count_p = (isofseblk - isofsblk + 1) * (2048/512);
364 		if (*block_count_p > max_block_count)
365 			errx(1, "CD9660: Too many blocks");
366 		if (verbose)
367 			printf("CD9660: %d block numbers: ", *block_count_p);
368 		for (i = 0; i < *block_count_p; i++) {
369 			blk = (isofsblk * (2048/512)) + i;
370 			block_table[i] = blk;
371 			if (verbose)
372 				printf("%d ", blk);
373 		}
374 		if (verbose)
375 			printf("\n");
376 		return 0;
377 	}
378 
379 	/*
380 	 * Open 2nd-level boot program and record the block numbers
381 	 * it occupies on the filesystem represented by `devfd'.
382 	 */
383 	if ((fd = open(boot, O_RDONLY)) < 0)
384 		err(1, "open: %s", boot);
385 
386 	if (fstatfs(fd, &statfsbuf) != 0)
387 		err(1, "statfs: %s", boot);
388 
389 	if (strncmp(statfsbuf.f_fstypename, "ffs", MFSNAMELEN) &&
390 	    strncmp(statfsbuf.f_fstypename, "ufs", MFSNAMELEN)) {
391 		errx(1, "%s: must be on an FFS filesystem", boot);
392 	}
393 
394 	if (fsync(fd) != 0)
395 		err(1, "fsync: %s", boot);
396 
397 	if (fstat(fd, &statbuf) != 0)
398 		err(1, "fstat: %s", boot);
399 
400 	close(fd);
401 
402 	/* Read superblock */
403 	devread(devfd, sblock, btodb(SBOFF), SBSIZE, "superblock");
404 	fs = (struct fs *)sblock;
405 
406 	/* Read inode */
407 	if ((buf = malloc(fs->fs_bsize)) == NULL)
408 		errx(1, "No memory for filesystem block");
409 
410 	blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino));
411 	devread(devfd, buf, blk, fs->fs_bsize, "inode");
412 	ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino);
413 
414 	/*
415 	 * Register filesystem block size.
416 	 */
417 	*block_size_p = fs->fs_bsize;
418 
419 	/*
420 	 * Get the block numbers; we don't handle fragments
421 	 */
422 	ndb = howmany(ip->di_size, fs->fs_bsize);
423 	if (ndb > max_block_count)
424 		errx(1, "%s: Too many blocks", boot);
425 
426 	/*
427 	 * Register block count.
428 	 */
429 	*block_count_p = ndb;
430 
431 	if (verbose)
432 		printf("%s: block numbers: ", boot);
433 	ap = ip->di_db;
434 	for (i = 0; i < NDADDR && *ap && ndb; i++, ap++, ndb--) {
435 		blk = fsbtodb(fs, *ap);
436 		block_table[i] = blk;
437 		if (verbose)
438 			printf("%d ", blk);
439 	}
440 	if (verbose)
441 		printf("\n");
442 
443 	if (ndb == 0)
444 		return 0;
445 
446 	/*
447 	 * Just one level of indirections; there isn't much room
448 	 * for more in the 1st-level bootblocks anyway.
449 	 */
450 	if (verbose)
451 		printf("%s: block numbers (indirect): ", boot);
452 	blk = ip->di_ib[0];
453 	devread(devfd, buf, blk, fs->fs_bsize, "indirect block");
454 	ap = (daddr_t *)buf;
455 	for (; i < NINDIR(fs) && *ap && ndb; i++, ap++, ndb--) {
456 		blk = fsbtodb(fs, *ap);
457 		block_table[i] = blk;
458 		if (verbose)
459 			printf("%d ", blk);
460 	}
461 	if (verbose)
462 		printf("\n");
463 
464 	if (ndb)
465 		errx(1, "%s: Too many blocks", boot);
466 	return 0;
467 }
468