xref: /openbsd/sys/arch/alpha/stand/installboot.c (revision db3296cf)
1 /*	$OpenBSD: installboot.c,v 1.11 2002/03/14 01:26:27 millert Exp $	*/
2 /*	$NetBSD: installboot.c,v 1.2 1997/04/06 08:41:12 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1997 Christopher G. Demetriou.  All rights reserved.
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/ioctl.h>
38 #include <sys/time.h>
39 #include <sys/stat.h>
40 #include <sys/sysctl.h>
41 #include <ufs/ufs/dinode.h>
42 #include <ufs/ufs/dir.h>
43 #include <ufs/ffs/fs.h>
44 #include <sys/disklabel.h>
45 #include <sys/dkio.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <util.h>
54 
55 #include "bbinfo.h"
56 
57 #ifndef	ISO_DEFAULT_BLOCK_SIZE
58 #define	ISO_DEFAULT_BLOCK_SIZE	2048
59 #endif
60 
61 int	verbose, nowrite, hflag;
62 char	*boot, *proto, *dev;
63 
64 struct bbinfoloc *bbinfolocp;
65 struct bbinfo *bbinfop;
66 int	max_block_count;
67 
68 
69 char		*loadprotoblocks(char *, long *);
70 int		loadblocknums(char *, int, unsigned long);
71 static void	devread(int, void *, daddr_t, size_t, char *);
72 static void	usage(void);
73 int 		main(int, char *[]);
74 
75 int	isofsblk = 0;
76 int	isofseblk = 0;
77 
78 static void
79 usage()
80 {
81 	(void)fprintf(stderr,
82 		"usage: installboot [-n] [-v] [-s isofsblk -e isofseblk] "
83 		"<boot> <proto> <device>\n");
84 	exit(1);
85 }
86 
87 int
88 main(argc, argv)
89 	int argc;
90 	char *argv[];
91 {
92 	int	c;
93 	int	devfd;
94 	char	*protostore;
95 	long	protosize;
96 	struct stat disksb, bootsb;
97 	struct disklabel dl;
98 	unsigned long partoffset;
99 
100 	while ((c = getopt(argc, argv, "vns:e:")) != -1) {
101 		switch (c) {
102 		case 'n':
103 			/* Do not actually write the bootblock to disk */
104 			nowrite = 1;
105 			break;
106 		case 'v':
107 			/* Chat */
108 			verbose = 1;
109 			break;
110 		case 's':
111 			isofsblk = atoi(optarg);
112 			break;
113 		case 'e':
114 			isofseblk = atoi(optarg);
115 			break;
116 		default:
117 			usage();
118 		}
119 	}
120 
121 	if (argc - optind < 3)
122 		usage();
123 
124 	boot = argv[optind];
125 	proto = argv[optind + 1];
126 	dev = argv[optind + 2];
127 
128 	if (verbose) {
129 		(void)printf("boot: %s\n", boot);
130 		(void)printf("proto: %s\n", proto);
131 		(void)printf("device: %s\n", dev);
132 	}
133 
134 	/* Load proto blocks into core */
135 	if ((protostore = loadprotoblocks(proto, &protosize)) == NULL)
136 		exit(1);
137 
138 	/* Open and check raw disk device */
139 	if ((devfd = opendev(dev, O_RDONLY, OPENDEV_PART, &dev)) < 0)
140 		err(1, "open: %s", dev);
141 	if (fstat(devfd, &disksb) == -1)
142 		err(1, "fstat: %s", dev);
143 	if (!S_ISCHR(disksb.st_mode))
144 		errx(1, "%s must be a character device node", dev);
145 	if ((minor(disksb.st_rdev) % getmaxpartitions()) != getrawpartition())
146 		errx(1, "%s must be the raw partition", dev);
147 
148 	/* Extract and load block numbers */
149 	if (stat(boot, &bootsb) == -1)
150 		err(1, "stat: %s", boot);
151 	if (!S_ISREG(bootsb.st_mode))
152 		errx(1, "%s must be a regular file", boot);
153 	if ((minor(disksb.st_rdev) / getmaxpartitions()) !=
154 	    (minor(bootsb.st_dev) / getmaxpartitions()))
155 		errx(1, "%s must be somewhere on %s", boot, dev);
156 
157 	/*
158 	 * Find the offset of the secondary boot block's partition
159 	 * into the disk.  If disklabels not supported, assume zero.
160 	 */
161 	if (ioctl(devfd, DIOCGDINFO, &dl) != -1) {
162 		partoffset = dl.d_partitions[minor(bootsb.st_dev) %
163 		    getmaxpartitions()].p_offset;
164 	} else {
165 		if (errno != ENOTTY)
166 			err(1, "read disklabel: %s", dev);
167 		warnx("couldn't read label from %s, using part offset of 0",
168 		    dev);
169 		partoffset = 0;
170 	}
171 	if (verbose)
172 		(void)printf("%s partition offset = 0x%lx\n", boot, partoffset);
173 
174 	/* Sync filesystems (make sure boot's block numbers are stable) */
175 	sync();
176 	sleep(2);
177 	sync();
178 	sleep(2);
179 
180 	if (loadblocknums(boot, devfd, partoffset) != 0)
181 		exit(1);
182 
183 	(void)close(devfd);
184 
185 	if (nowrite)
186 		return 0;
187 
188 #if 0
189 	/* Write patched proto bootblocks into the superblock */
190 	if (protosize > SBSIZE - DEV_BSIZE)
191 		errx(1, "proto bootblocks too big");
192 #endif
193 
194 	if ((devfd = opendev(dev, O_RDWR, OPENDEV_PART, &dev)) < 0)
195 		err(1, "open: %s", dev);
196 
197 	if (lseek(devfd, DEV_BSIZE, SEEK_SET) != DEV_BSIZE)
198 		err(1, "lseek bootstrap");
199 
200 	if (write(devfd, protostore, protosize) != protosize)
201 		err(1, "write bootstrap");
202 
203 	{
204 
205 #define BBPAD   0x1e0
206 	struct bb {
207 		char    bb_pad[BBPAD];  /* disklabel lives in here, actually */
208 		long    bb_secsize;     /* size of secondary boot block */
209 		long    bb_secstart;    /* start of secondary boot block */
210 		long    bb_flags;       /* unknown; always zero */
211 		long    bb_cksum;       /* checksum of the boot block, as longs. */
212 	} bb;
213 	long *lp, *ep;
214 
215 	if (lseek(devfd, 0, SEEK_SET) != 0)
216 		err(1, "lseek label");
217 
218 	if (read(devfd, &bb, sizeof (bb)) != sizeof (bb))
219 		err(1, "read label");
220 
221         bb.bb_secsize = 15;
222         bb.bb_secstart = 1;
223         bb.bb_flags = 0;
224         bb.bb_cksum = 0;
225 
226         for (lp = (long *)&bb, ep = &bb.bb_cksum; lp < ep; lp++)
227                 bb.bb_cksum += *lp;
228 
229 	if (lseek(devfd, 0, SEEK_SET) != 0)
230 		err(1, "lseek label 2");
231 
232         if (write(devfd, &bb, sizeof bb) != sizeof bb)
233 		err(1, "write label ");
234 	}
235 
236 	(void)close(devfd);
237 	return 0;
238 }
239 
240 char *
241 loadprotoblocks(fname, size)
242 	char *fname;
243 	long *size;
244 {
245 	int	fd, sz;
246 	char	*bp;
247 	struct	stat statbuf;
248 	u_int64_t *matchp;
249 
250 	/*
251 	 * Read the prototype boot block into memory.
252 	 */
253 	if ((fd = open(fname, O_RDONLY)) < 0) {
254 		warn("open: %s", fname);
255 		return NULL;
256 	}
257 	if (fstat(fd, &statbuf) != 0) {
258 		warn("fstat: %s", fname);
259 		close(fd);
260 		return NULL;
261 	}
262 	sz = roundup(statbuf.st_size, DEV_BSIZE);
263 	if ((bp = calloc(sz, 1)) == NULL) {
264 		warnx("malloc: %s: no memory", fname);
265 		close(fd);
266 		return NULL;
267 	}
268 	if (read(fd, bp, statbuf.st_size) != statbuf.st_size) {
269 		warn("read: %s", fname);
270 		free(bp);
271 		close(fd);
272 		return NULL;
273 	}
274 	close(fd);
275 
276 	/*
277 	 * Find the magic area of the program, and figure out where
278 	 * the 'blocks' struct is, from that.
279 	 */
280 	bbinfolocp = NULL;
281 	for (matchp = (u_int64_t *)bp; (char *)matchp < bp + sz; matchp++) {
282 		if (*matchp != 0xbabefacedeadbeef)
283 			continue;
284 		bbinfolocp = (struct bbinfoloc *)matchp;
285 		if (bbinfolocp->magic1 == 0xbabefacedeadbeef &&
286 		    bbinfolocp->magic2 == 0xdeadbeeffacebabe)
287 			break;
288 		bbinfolocp = NULL;
289 	}
290 
291 	if (bbinfolocp == NULL) {
292 		warnx("%s: not a valid boot block?", fname);
293 		return NULL;
294 	}
295 
296 	bbinfop = (struct bbinfo *)(bp + bbinfolocp->end - bbinfolocp->start);
297 	memset(bbinfop, 0, sz - (bbinfolocp->end - bbinfolocp->start));
298 	max_block_count =
299 	    ((char *)bbinfop->blocks - bp) / sizeof (bbinfop->blocks[0]);
300 
301 	if (verbose) {
302 		(void)printf("boot block info locator at offset 0x%x\n",
303 			(char *)bbinfolocp - bp);
304 		(void)printf("boot block info at offset 0x%x\n",
305 			(char *)bbinfop - bp);
306 		(void)printf("max number of blocks: %d\n", max_block_count);
307 	}
308 
309 	*size = sz;
310 	return (bp);
311 }
312 
313 static void
314 devread(fd, buf, blk, size, msg)
315 	int	fd;
316 	void	*buf;
317 	daddr_t	blk;
318 	size_t	size;
319 	char	*msg;
320 {
321 	if (lseek(fd, dbtob(blk), SEEK_SET) != dbtob(blk))
322 		err(1, "%s: devread: lseek", msg);
323 
324 	if (read(fd, buf, size) != size)
325 		err(1, "%s: devread: read", msg);
326 }
327 
328 static char sblock[SBSIZE];
329 
330 int
331 loadblocknums(boot, devfd, partoffset)
332 	char	*boot;
333 	int	devfd;
334 	unsigned long partoffset;
335 {
336 	int		i, fd;
337 	struct	stat	statbuf;
338 	struct	statfs	statfsbuf;
339 	struct fs	*fs;
340 	char		*buf;
341 	daddr_t		blk, *ap;
342 	struct dinode	*ip;
343 	int		ndb;
344 	int32_t		cksum;
345 
346 	/*
347 	 * Open 2nd-level boot program and record the block numbers
348 	 * it occupies on the filesystem represented by `devfd'.
349 	 */
350 	if ((fd = open(boot, O_RDONLY)) < 0)
351 		err(1, "open: %s", boot);
352 
353 	if (fstatfs(fd, &statfsbuf) != 0)
354 		err(1, "statfs: %s", boot);
355 
356 	if (isofsblk) {
357 		bbinfop->bsize = ISO_DEFAULT_BLOCK_SIZE;
358 		bbinfop->nblocks = isofseblk - isofsblk + 1;
359 		if (bbinfop->nblocks > max_block_count)
360 			errx(1, "%s: Too many blocks", boot);
361 		if (verbose)
362 			(void)printf("%s: starting block %d (%d total):\n\t",
363 			    boot, isofsblk, bbinfop->nblocks);
364 		for (i = 0; i < bbinfop->nblocks; i++) {
365 			blk = (isofsblk + i) * (bbinfop->bsize / DEV_BSIZE);
366 			bbinfop->blocks[i] = blk;
367 			if (verbose)
368 				(void)printf("%d ", blk);
369 		}
370 		if (verbose)
371 			(void)printf("\n");
372 
373 		cksum = 0;
374 		for (i = 0; i < bbinfop->nblocks +
375 		    (sizeof(*bbinfop) / sizeof(bbinfop->blocks[0])) - 1; i++)
376 			cksum += ((int32_t *)bbinfop)[i];
377 		bbinfop->cksum = -cksum;
378 
379 		return 0;
380 	}
381 
382 	if (strncmp(statfsbuf.f_fstypename, MOUNT_FFS, MFSNAMELEN))
383 		errx(1, "%s: must be on a FFS filesystem", boot);
384 
385 	if (fsync(fd) != 0)
386 		err(1, "fsync: %s", boot);
387 
388 	if (fstat(fd, &statbuf) != 0)
389 		err(1, "fstat: %s", boot);
390 
391 	close(fd);
392 
393 	/* Read superblock */
394 	devread(devfd, sblock, btodb(SBOFF) + partoffset, SBSIZE,
395 	    "superblock");
396 	fs = (struct fs *)sblock;
397 
398 	/* Read inode */
399 	if ((buf = malloc(fs->fs_bsize)) == NULL)
400 		errx(1, "No memory for filesystem block");
401 
402 	blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino));
403 	devread(devfd, buf, blk + partoffset, fs->fs_bsize, "inode");
404 	ip = (struct dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino);
405 
406 	/*
407 	 * Register filesystem block size.
408 	 */
409 	bbinfop->bsize = fs->fs_bsize;
410 
411 	/*
412 	 * Get the block numbers; we don't handle fragments
413 	 */
414 	ndb = howmany(ip->di_size, fs->fs_bsize);
415 	if (ndb > max_block_count)
416 		errx(1, "%s: Too many blocks", boot);
417 
418 	/*
419 	 * Register block count.
420 	 */
421 	bbinfop->nblocks = ndb;
422 
423 	if (verbose)
424 		(void)printf("%s: block numbers: ", boot);
425 	ap = ip->di_db;
426 	for (i = 0; i < NDADDR && *ap && ndb; i++, ap++, ndb--) {
427 		blk = fsbtodb(fs, *ap);
428 		bbinfop->blocks[i] = blk + partoffset;
429 		if (verbose)
430 			(void)printf("%d ", bbinfop->blocks[i]);
431 	}
432 	if (verbose)
433 		(void)printf("\n");
434 
435 	if (ndb == 0)
436 		goto checksum;
437 
438 	/*
439 	 * Just one level of indirections; there isn't much room
440 	 * for more in the 1st-level bootblocks anyway.
441 	 */
442 	if (verbose)
443 		(void)printf("%s: block numbers (indirect): ", boot);
444 	blk = ip->di_ib[0];
445 	devread(devfd, buf, blk + partoffset, fs->fs_bsize,
446 	    "indirect block");
447 	ap = (daddr_t *)buf;
448 	for (; i < NINDIR(fs) && *ap && ndb; i++, ap++, ndb--) {
449 		blk = fsbtodb(fs, *ap);
450 		bbinfop->blocks[i] = blk + partoffset;
451 		if (verbose)
452 			(void)printf("%d ", bbinfop->blocks[i]);
453 	}
454 	if (verbose)
455 		(void)printf("\n");
456 
457 	if (ndb)
458 		errx(1, "%s: Too many blocks", boot);
459 
460 checksum:
461 	cksum = 0;
462 	for (i = 0; i < bbinfop->nblocks +
463 	    (sizeof (*bbinfop) / sizeof (bbinfop->blocks[0])) - 1; i++) {
464 		cksum += ((int32_t *)bbinfop)[i];
465 	}
466 	bbinfop->cksum = -cksum;
467 
468 	return 0;
469 }
470