xref: /openbsd/sys/arch/alpha/stand/installboot.c (revision 108bc87a)
1 /*	$OpenBSD: installboot.c,v 1.20 2020/03/11 09:59:31 otto 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 static int	sbchk(struct fs *, daddr_t);
74 static void	sbread(int, daddr_t, struct fs **, char *);
75 int		main(int, char *[]);
76 
77 int	isofsblk = 0;
78 int	isofseblk = 0;
79 
80 static const daddr_t sbtry[] = SBLOCKSEARCH;
81 
82 static void
usage(void)83 usage(void)
84 {
85 	(void)fprintf(stderr,
86 	    "usage: installboot [-n] [-v] [-s isofsblk -e isofseblk] "
87 	    "<boot> <proto> <device>\n");
88 	exit(1);
89 }
90 
91 int
main(int argc,char * argv[])92 main(int argc, char *argv[])
93 {
94 	int	c, devfd;
95 	char	*protostore;
96 	long	protosize;
97 	struct stat disksb, bootsb;
98 	struct disklabel dl;
99 	daddr_t partoffset;
100 #define BBPAD   0x1e0
101 	struct bb {
102 		char	bb_pad[BBPAD];	/* disklabel lives in here, actually */
103 		long	bb_secsize;	/* size of secondary boot block */
104 		long	bb_secstart;	/* start of secondary boot block */
105 		long	bb_flags;	/* unknown; always zero */
106 		long	bb_cksum;	/* checksum of the boot block, as longs. */
107 	} bb;
108 	long *lp, *ep;
109 
110 	while ((c = getopt(argc, argv, "vns:e:")) != -1) {
111 		switch (c) {
112 		case 'n':
113 			/* Do not actually write the bootblock to disk */
114 			nowrite = 1;
115 			break;
116 		case 'v':
117 			/* Chat */
118 			verbose = 1;
119 			break;
120 		case 's':
121 			isofsblk = atoi(optarg);
122 			break;
123 		case 'e':
124 			isofseblk = atoi(optarg);
125 			break;
126 		default:
127 			usage();
128 		}
129 	}
130 
131 	if (argc - optind < 3)
132 		usage();
133 
134 	boot = argv[optind];
135 	proto = argv[optind + 1];
136 	dev = argv[optind + 2];
137 
138 	if (verbose) {
139 		(void)printf("boot: %s\n", boot);
140 		(void)printf("proto: %s\n", proto);
141 		(void)printf("device: %s\n", dev);
142 	}
143 
144 	/* Load proto blocks into core */
145 	if ((protostore = loadprotoblocks(proto, &protosize)) == NULL)
146 		exit(1);
147 
148 	/* Open and check raw disk device */
149 	if ((devfd = opendev(dev, O_RDONLY, OPENDEV_PART, &dev)) < 0)
150 		err(1, "open: %s", dev);
151 	if (fstat(devfd, &disksb) == -1)
152 		err(1, "fstat: %s", dev);
153 	if (!S_ISCHR(disksb.st_mode))
154 		errx(1, "%s must be a character device node", dev);
155 	if ((minor(disksb.st_rdev) % getmaxpartitions()) != getrawpartition())
156 		errx(1, "%s must be the raw partition", dev);
157 
158 	/* Extract and load block numbers */
159 	if (stat(boot, &bootsb) == -1)
160 		err(1, "stat: %s", boot);
161 	if (!S_ISREG(bootsb.st_mode))
162 		errx(1, "%s must be a regular file", boot);
163 	if ((minor(disksb.st_rdev) / getmaxpartitions()) !=
164 	    (minor(bootsb.st_dev) / getmaxpartitions()))
165 		errx(1, "%s must be somewhere on %s", boot, dev);
166 
167 	/*
168 	 * Find the offset of the secondary boot block's partition
169 	 * into the disk.  If disklabels not supported, assume zero.
170 	 */
171 	if (ioctl(devfd, DIOCGDINFO, &dl) != -1) {
172 		partoffset = DL_GETPOFFSET(&dl.d_partitions[minor(bootsb.st_dev) %
173 		    getmaxpartitions()]);
174 	} else {
175 		if (errno != ENOTTY)
176 			err(1, "read disklabel: %s", dev);
177 		warnx("couldn't read label from %s, using part offset of 0",
178 		    dev);
179 		partoffset = 0;
180 	}
181 	if (verbose)
182 		(void)printf("%s partition offset = 0x%llx\n", boot, partoffset);
183 
184 	/* Sync filesystems (make sure boot's block numbers are stable) */
185 	sync();
186 	sleep(2);
187 	sync();
188 	sleep(2);
189 
190 	if (loadblocknums(boot, devfd, DL_SECTOBLK(&dl, partoffset)) != 0)
191 		exit(1);
192 
193 	(void)close(devfd);
194 
195 	if (nowrite)
196 		return 0;
197 
198 #if 0
199 	/* Write patched proto bootblocks into the superblock */
200 	if (protosize > SBSIZE - DEV_BSIZE)
201 		errx(1, "proto bootblocks too big");
202 #endif
203 
204 	if ((devfd = opendev(dev, O_RDWR, OPENDEV_PART, &dev)) < 0)
205 		err(1, "open: %s", dev);
206 
207 	if (lseek(devfd, DEV_BSIZE, SEEK_SET) != DEV_BSIZE)
208 		err(1, "lseek bootstrap");
209 
210 	if (write(devfd, protostore, protosize) != protosize)
211 		err(1, "write bootstrap");
212 
213 	if (lseek(devfd, 0, SEEK_SET) != 0)
214 		err(1, "lseek label");
215 
216 	if (read(devfd, &bb, sizeof (bb)) != sizeof (bb))
217 		err(1, "read label");
218 
219 	bb.bb_secsize = 15;
220 	bb.bb_secstart = 1;
221 	bb.bb_flags = 0;
222 	bb.bb_cksum = 0;
223 
224 	for (lp = (long *)&bb, ep = &bb.bb_cksum; lp < ep; lp++)
225 		bb.bb_cksum += *lp;
226 
227 	if (lseek(devfd, 0, SEEK_SET) != 0)
228 		err(1, "lseek label 2");
229 
230 	if (write(devfd, &bb, sizeof bb) != sizeof bb)
231 		err(1, "write label ");
232 
233 	(void)close(devfd);
234 	return 0;
235 }
236 
237 char *
loadprotoblocks(char * fname,long * size)238 loadprotoblocks(char *fname, long *size)
239 {
240 	int	fd, sz;
241 	char	*bp;
242 	struct	stat statbuf;
243 	u_int64_t *matchp;
244 
245 	/*
246 	 * Read the prototype boot block into memory.
247 	 */
248 	if ((fd = open(fname, O_RDONLY)) < 0) {
249 		warn("open: %s", fname);
250 		return NULL;
251 	}
252 	if (fstat(fd, &statbuf) != 0) {
253 		warn("fstat: %s", fname);
254 		close(fd);
255 		return NULL;
256 	}
257 	sz = roundup(statbuf.st_size, DEV_BSIZE);
258 	if ((bp = calloc(sz, 1)) == NULL) {
259 		warnx("malloc: %s: no memory", fname);
260 		close(fd);
261 		return NULL;
262 	}
263 	if (read(fd, bp, statbuf.st_size) != statbuf.st_size) {
264 		warn("read: %s", fname);
265 		free(bp);
266 		close(fd);
267 		return NULL;
268 	}
269 	close(fd);
270 
271 	/*
272 	 * Find the magic area of the program, and figure out where
273 	 * the 'blocks' struct is, from that.
274 	 */
275 	bbinfolocp = NULL;
276 	for (matchp = (u_int64_t *)bp; (char *)matchp < bp + sz; matchp++) {
277 		if (*matchp != 0xbabefacedeadbeef)
278 			continue;
279 		bbinfolocp = (struct bbinfoloc *)matchp;
280 		if (bbinfolocp->magic1 == 0xbabefacedeadbeef &&
281 		    bbinfolocp->magic2 == 0xdeadbeeffacebabe)
282 			break;
283 		bbinfolocp = NULL;
284 	}
285 
286 	if (bbinfolocp == NULL) {
287 		warnx("%s: not a valid boot block?", fname);
288 		return NULL;
289 	}
290 
291 	bbinfop = (struct bbinfo *)(bp + bbinfolocp->end - bbinfolocp->start);
292 	memset(bbinfop, 0, sz - (bbinfolocp->end - bbinfolocp->start));
293 	max_block_count =
294 	    ((char *)bbinfop->blocks - bp) / sizeof (bbinfop->blocks[0]);
295 
296 	if (verbose) {
297 		(void)printf("boot block info locator at offset 0x%x\n",
298 			(char *)bbinfolocp - bp);
299 		(void)printf("boot block info at offset 0x%x\n",
300 			(char *)bbinfop - bp);
301 		(void)printf("max number of blocks: %d\n", max_block_count);
302 	}
303 
304 	*size = sz;
305 	return (bp);
306 }
307 
308 static void
devread(int fd,void * buf,daddr_t blk,size_t size,char * msg)309 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
310 {
311 	if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size)
312 		err(1, "%s: devread: pread", msg);
313 }
314 
315 static char sblock[SBSIZE];
316 
317 int
loadblocknums(char * boot,int devfd,unsigned long partoffset)318 loadblocknums(char *boot, int devfd, unsigned long partoffset)
319 {
320 	int		i, fd, ndb;
321 	struct	stat	statbuf;
322 	struct	statfs	statfsbuf;
323 	struct fs	*fs;
324 	char		*buf;
325 	daddr32_t	*ap1;
326 	daddr_t		blk, *ap2;
327 	struct ufs1_dinode	*ip1;
328 	struct ufs2_dinode	*ip2;
329 	int32_t		cksum;
330 
331 	/*
332 	 * Open 2nd-level boot program and record the block numbers
333 	 * it occupies on the filesystem represented by `devfd'.
334 	 */
335 	if ((fd = open(boot, O_RDONLY)) < 0)
336 		err(1, "open: %s", boot);
337 
338 	if (fstatfs(fd, &statfsbuf) != 0)
339 		err(1, "statfs: %s", boot);
340 
341 	if (isofsblk) {
342 		bbinfop->bsize = ISO_DEFAULT_BLOCK_SIZE;
343 		bbinfop->nblocks = isofseblk - isofsblk + 1;
344 		if (bbinfop->nblocks > max_block_count)
345 			errx(1, "%s: Too many blocks", boot);
346 		if (verbose)
347 			(void)printf("%s: starting block %d (%d total):\n\t",
348 			    boot, isofsblk, bbinfop->nblocks);
349 		for (i = 0; i < bbinfop->nblocks; i++) {
350 			blk = (isofsblk + i) * (bbinfop->bsize / DEV_BSIZE);
351 			bbinfop->blocks[i] = blk;
352 			if (verbose)
353 				(void)printf("%d ", blk);
354 		}
355 		if (verbose)
356 			(void)printf("\n");
357 
358 		cksum = 0;
359 		for (i = 0; i < bbinfop->nblocks +
360 		    (sizeof(*bbinfop) / sizeof(bbinfop->blocks[0])) - 1; i++)
361 			cksum += ((int32_t *)bbinfop)[i];
362 		bbinfop->cksum = -cksum;
363 
364 		return 0;
365 	}
366 
367 	if (strncmp(statfsbuf.f_fstypename, MOUNT_FFS, MFSNAMELEN))
368 		errx(1, "%s: must be on a FFS filesystem", boot);
369 
370 	if (fsync(fd) != 0)
371 		err(1, "fsync: %s", boot);
372 
373 	if (fstat(fd, &statbuf) != 0)
374 		err(1, "fstat: %s", boot);
375 
376 	close(fd);
377 
378 	/* Read superblock */
379 	sbread(devfd, partoffset, &fs, sblock);
380 
381 	/* Read inode */
382 	if ((buf = malloc(fs->fs_bsize)) == NULL)
383 		errx(1, "No memory for filesystem block");
384 
385 	blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino));
386 	devread(devfd, buf, blk + partoffset, fs->fs_bsize, "inode");
387 	if (fs->fs_magic == FS_UFS1_MAGIC) {
388 		ip1 = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs,
389 		    statbuf.st_ino);
390 		ndb = howmany(ip1->di_size, fs->fs_bsize);
391 	} else {
392 		ip2 = (struct ufs2_dinode *)(buf) + ino_to_fsbo(fs,
393 		    statbuf.st_ino);
394 		ndb = howmany(ip2->di_size, fs->fs_bsize);
395 	}
396 	/*
397 	 * Check the block numbers; we don't handle fragments
398 	 */
399 	if (ndb > max_block_count)
400 		errx(1, "%s: Too many blocks", boot);
401 
402 	/*
403 	 * Register filesystem block size.
404 	 */
405 	bbinfop->bsize = fs->fs_bsize;
406 
407 	/*
408 	 * Register block count.
409 	 */
410 	bbinfop->nblocks = ndb;
411 
412 	if (verbose)
413 		(void)printf("%s: block numbers: ", boot);
414 	if (fs->fs_magic == FS_UFS1_MAGIC) {
415 		ap1 = ip1->di_db;
416 		for (i = 0; i < NDADDR && *ap1 && ndb; i++, ap1++, ndb--) {
417 			blk = fsbtodb(fs, *ap1);
418 			bbinfop->blocks[i] = blk + partoffset;
419 			if (verbose)
420 				(void)printf("%d ", bbinfop->blocks[i]);
421 		}
422 	} else {
423 		ap2 = ip2->di_db;
424 		for (i = 0; i < NDADDR && *ap2 && ndb; i++, ap2++, ndb--) {
425 			blk = fsbtodb(fs, *ap2);
426 			bbinfop->blocks[i] = blk + partoffset;
427 			if (verbose)
428 				(void)printf("%d ", bbinfop->blocks[i]);
429 		}
430 	}
431 	if (verbose)
432 		(void)printf("\n");
433 
434 	if (ndb == 0)
435 		goto checksum;
436 
437 	/*
438 	 * Just one level of indirections; there isn't much room
439 	 * for more in the 1st-level bootblocks anyway.
440 	 */
441 	if (verbose)
442 		(void)printf("%s: block numbers (indirect): ", boot);
443 	if (fs->fs_magic == FS_UFS1_MAGIC) {
444 		blk = ip1->di_ib[0];
445 		devread(devfd, buf, blk + partoffset, fs->fs_bsize,
446 		    "indirect block");
447 		ap1 = (daddr32_t *)buf;
448 		for (; i < NINDIR(fs) && *ap1 && ndb; i++, ap1++, ndb--) {
449 			blk = fsbtodb(fs, *ap1);
450 			bbinfop->blocks[i] = blk + partoffset;
451 			if (verbose)
452 				(void)printf("%d ", bbinfop->blocks[i]);
453 		}
454 	} else {
455 		blk = ip2->di_ib[0];
456 		devread(devfd, buf, blk + partoffset, fs->fs_bsize,
457 		    "indirect block");
458 		ap2 = (daddr_t *)buf;
459 		for (; i < NINDIR(fs) && *ap2 && ndb; i++, ap2++, ndb--) {
460 			blk = fsbtodb(fs, *ap2);
461 			bbinfop->blocks[i] = blk + partoffset;
462 			if (verbose)
463 				(void)printf("%d ", bbinfop->blocks[i]);
464 		}
465 	}
466 	if (verbose)
467 		(void)printf("\n");
468 
469 	if (ndb)
470 		errx(1, "%s: Too many blocks", boot);
471 
472 checksum:
473 	cksum = 0;
474 	for (i = 0; i < bbinfop->nblocks +
475 	    (sizeof (*bbinfop) / sizeof (bbinfop->blocks[0])) - 1; i++)
476 		cksum += ((int32_t *)bbinfop)[i];
477 	bbinfop->cksum = -cksum;
478 
479 	return 0;
480 }
481 
482 static int
sbchk(struct fs * fs,daddr_t sbloc)483 sbchk(struct fs *fs, daddr_t sbloc)
484 {
485 	if (verbose)
486 		fprintf(stderr, "looking for superblock at %lld\n", sbloc);
487 
488 	if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) {
489 		if (verbose)
490 			fprintf(stderr, "bad superblock magic 0x%x\n",
491 			    fs->fs_magic);
492 		return (0);
493 	}
494 
495 	/*
496 	 * Looking for an FFS1 file system at SBLOCK_UFS2 will find the
497 	 * wrong superblock for file systems with 64k block size.
498 	 */
499 	if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) {
500 		if (verbose)
501 			fprintf(stderr, "skipping ffs1 superblock at %lld\n",
502 			    sbloc);
503 		return (0);
504 	}
505 
506 	if (fs->fs_bsize <= 0 || fs->fs_bsize < sizeof(struct fs) ||
507 	    fs->fs_bsize > MAXBSIZE) {
508 		if (verbose)
509 			fprintf(stderr, "invalid superblock block size %d\n",
510 			    fs->fs_bsize);
511 		return (0);
512 	}
513 
514 	if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) {
515 		if (verbose)
516 			fprintf(stderr, "invalid superblock size %d\n",
517 			    fs->fs_sbsize);
518 		return (0);
519 	}
520 
521 	if (fs->fs_inopb <= 0) {
522 		if (verbose)
523 			fprintf(stderr, "invalid superblock inodes/block %d\n",
524 			    fs->fs_inopb);
525 		return (0);
526 	}
527 
528 	if (verbose)
529 		fprintf(stderr, "found valid %s superblock\n",
530 		    fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1");
531 
532 	return (1);
533 }
534 
535 static void
sbread(int fd,daddr_t poffset,struct fs ** fs,char * sblock)536 sbread(int fd, daddr_t poffset, struct fs **fs, char *sblock)
537 {
538 	int i;
539 	daddr_t sboff;
540 
541 	for (i = 0; sbtry[i] != -1; i++) {
542 		sboff = sbtry[i] / DEV_BSIZE;
543 		devread(fd, sblock, poffset + sboff, SBSIZE, "superblock");
544 		*fs = (struct fs *)sblock;
545 		if (sbchk(*fs, sbtry[i]))
546 			break;
547 	}
548 
549 	if (sbtry[i] == -1)
550 		errx(1, "couldn't find ffs superblock");
551 }
552