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