1 /*	$NetBSD: installboot.c,v 1.15 2002/08/09 10:08:13 soren Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Waldi Ravens
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by Waldi Ravens.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/sysctl.h>
36 #include <sys/ioctl.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <paths.h>
42 #include <fcntl.h>
43 #include <errno.h>
44 #include <err.h>
45 #include <limits.h>
46 #include <nlist.h>
47 #include <kvm.h>
48 
49 #define	DKTYPENAMES
50 #define	FSTYPENAMES
51 #include <sys/disklabel.h>
52 #include <machine/ahdilabel.h>
53 
54 #include "installboot.h"
55 
56 static void	usage __P((void));
57 static void	oscheck __P((void));
58 static u_int	abcksum __P((void *));
59 static void	setNVpref __P((void));
60 static void	setIDEpar __P((u_int8_t *, size_t));
61 static void	mkahdiboot __P((struct ahdi_root *, char *,
62 						char *, daddr_t));
63 static void	mkbootblock __P((struct bootblock *, char *,
64 				char *, struct disklabel *, u_int));
65 static void	install_fd __P((char *, struct disklabel *));
66 static void	install_sd __P((char *, struct disklabel *));
67 static void	install_wd __P((char *, struct disklabel *));
68 
69 static struct bootblock	bootarea;
70 static struct ahdi_root ahdiboot;
71 static const char	mdecpath[] = PATH_MDEC;
72 static int		nowrite = 0;
73 static int		verbose = 0;
74 static int		trackpercyl = 0;
75 static int		secpertrack = 0;
76 
77 static void
78 usage ()
79 {
80 	fprintf(stderr,
81 		"usage: installboot [options] device\n"
82 		"where options are:\n"
83 		"\t-N  do not actually write anything on the disk\n"
84 		"\t-t  number of tracks per cylinder (IDE disk)\n"
85 		"\t-u  number of sectors per track (IDE disk)\n"
86 		"\t-v  verbose mode\n");
87 	exit(EXIT_FAILURE);
88 }
89 
90 int
91 main (argc, argv)
92 	int	argc;
93 	char	*argv[];
94 {
95 	struct disklabel dl;
96 	char		 *dn;
97 	int		 fd, c;
98 
99 	/* check OS bootversion */
100 	oscheck();
101 
102 	/* parse options */
103 	while ((c = getopt(argc, argv, "Nt:u:v")) != -1) {
104 		switch (c) {
105 		  case 'N':
106 			nowrite = 1;
107 			break;
108 		  case 't':
109 			trackpercyl = atoi(optarg);
110 			break;
111 		  case 'u':
112 			secpertrack = atoi(optarg);
113 			break;
114 		  case 'v':
115 			verbose = 1;
116 			break;
117 		  default:
118 			usage();
119 		}
120 	}
121 	argv += optind;
122 	argc -= optind;
123 	if (argc != 1)
124 		usage();
125 
126 	/* get disk label */
127 	dn = alloca(sizeof(_PATH_DEV) + strlen(argv[0]) + 8);
128 	if (!strchr(argv[0], '/')) {
129 		sprintf(dn, "%sr%s%c", _PATH_DEV, argv[0], RAW_PART + 'a');
130 		fd = open(dn, O_RDONLY);
131 		if (fd < 0 && errno == ENOENT) {
132 			sprintf(dn, "%sr%s", _PATH_DEV, argv[0]);
133 			fd = open(dn, O_RDONLY);
134 		}
135 	} else {
136 		sprintf(dn, "%s", argv[0]);
137 		fd = open(dn, O_RDONLY);
138 	}
139 	if (fd < 0)
140 		err(EXIT_FAILURE, "%s", dn);
141 	if (ioctl(fd, DIOCGDINFO, &dl))
142 		err(EXIT_FAILURE, "%s: DIOCGDINFO", dn);
143 	if (close(fd))
144 		err(EXIT_FAILURE, "%s", dn);
145 
146 	switch (dl.d_type) {
147 		case DTYPE_FLOPPY:
148 			install_fd(dn, &dl);
149 			break;
150 		case DTYPE_ST506:
151 		case DTYPE_ESDI:
152 			install_wd(dn, &dl);
153 			setNVpref();
154 			break;
155 		case DTYPE_SCSI:
156 			install_sd(dn, &dl);
157 			setNVpref();
158 			break;
159 		default:
160 			errx(EXIT_FAILURE,
161 			     "%s: %s: Device type not supported.",
162 			     dn, dktypenames[dl.d_type]);
163 	}
164 
165 	return(EXIT_SUCCESS);
166 }
167 
168 static void
169 oscheck ()
170 {
171 	struct nlist	kbv[] = { { "_bootversion" },
172 				  { NULL } };
173 	kvm_t		*kd_kern;
174 	char		errbuf[_POSIX2_LINE_MAX];
175 	u_short		kvers;
176 	struct stat	sb;
177 
178 	if (stat(_PATH_UNIX, &sb) < 0) {
179 		warnx("Cannot stat %s, no bootversion check done", _PATH_UNIX);
180 		return;
181 	}
182 
183 	kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
184 	if (kd_kern == NULL)
185 		errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf);
186 	if (kvm_nlist(kd_kern, kbv) == -1)
187 		errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd_kern));
188 	if (kbv[0].n_value == 0)
189 		errx(EXIT_FAILURE, "%s not in namelist", kbv[0].n_name);
190 	if (kvm_read(kd_kern, kbv[0].n_value, (char *)&kvers,
191 						sizeof(kvers)) == -1)
192 		errx(EXIT_FAILURE, "kvm_read: %s", kvm_geterr(kd_kern));
193 	kvm_close(kd_kern);
194 	if (kvers != BOOTVERSION)
195 		errx(EXIT_FAILURE, "Kern bootversion: %d, expected: %d",
196 					kvers, BOOTVERSION);
197 }
198 
199 static void
200 install_fd (devnm, label)
201 	char		 *devnm;
202 	struct disklabel *label;
203 {
204 	char		 *xxboot, *bootxx;
205 	struct partition *rootpart;
206 
207 	if (label->d_secsize != 512)
208 		errx(EXIT_FAILURE,
209 		     "%s: %u: Block size not supported.", devnm,
210 							  label->d_secsize);
211 	if (label->d_ntracks != 2)
212 		errx(EXIT_FAILURE,
213 		     "%s: Single sided floppy not supported.", devnm);
214 
215 	xxboot = alloca(strlen(mdecpath) + 8);
216 	sprintf(xxboot, "%sfdboot", mdecpath);
217 	bootxx = alloca(strlen(mdecpath) + 8);
218 	sprintf(bootxx, "%sbootxx", mdecpath);
219 
220 	/* first used partition (a, b or c) */		/* XXX */
221 	for (rootpart = label->d_partitions; ; ++rootpart) {
222 		if (rootpart->p_size)
223 			break;
224 	}
225 	if (rootpart != label->d_partitions) {		/* XXX */
226 		*(label->d_partitions) = *rootpart;
227 		memset(rootpart, 0, sizeof(*rootpart));
228 	}
229 	label->d_partitions->p_fstype = FS_BSDFFS;	/* XXX */
230 	label->d_npartitions = 1;
231 	label->d_checksum = 0;
232 	label->d_checksum = dkcksum(label);
233 
234 	trackpercyl = secpertrack = 0;
235 	mkbootblock(&bootarea, xxboot, bootxx, label, 0);
236 
237 	if (!nowrite) {
238 		int	fd;
239 		if ((fd = open(devnm, O_WRONLY)) < 0)
240 			err(EXIT_FAILURE, "%s", devnm);
241 		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
242 			err(EXIT_FAILURE, "%s", devnm);
243 		if (close(fd))
244 			err(EXIT_FAILURE, "%s", devnm);
245 		if (verbose)
246 			printf("Boot block installed on %s\n", devnm);
247 	}
248 }
249 
250 static void
251 install_sd (devnm, label)
252 	char		 *devnm;
253 	struct disklabel *label;
254 {
255 	char		 *xxb00t, *xxboot, *bootxx;
256 	struct disklabel rawlabel;
257 	daddr_t		 bbsec;
258 	u_int		 magic;
259 
260 	if (label->d_partitions[0].p_size == 0)
261 		errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
262 	if (label->d_partitions[0].p_fstype != FS_BSDFFS)
263 		errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
264 		     devnm, fstypenames[label->d_partitions[0].p_fstype]);
265 
266 	bbsec = readdisklabel(devnm, &rawlabel);
267 	if (bbsec == NO_BOOT_BLOCK)
268 		errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
269 	if (memcmp(label, &rawlabel, sizeof(*label)))
270 		errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
271 
272 	if (bbsec) {
273 		xxb00t = alloca(strlen(mdecpath) + 14);
274 		sprintf(xxb00t, "%ssdb00t.ahdi", mdecpath);
275 		xxboot = alloca(strlen(mdecpath) + 14);
276 		sprintf(xxboot, "%sxxboot.ahdi", mdecpath);
277 		magic = AHDIMAGIC;
278 	} else {
279 		xxb00t = NULL;
280 		xxboot = alloca(strlen(mdecpath) + 8);
281 		sprintf(xxboot, "%ssdboot", mdecpath);
282 		magic = NBDAMAGIC;
283 	}
284 	bootxx = alloca(strlen(mdecpath) + 8);
285 	sprintf(bootxx, "%sbootxx", mdecpath);
286 
287 	trackpercyl = secpertrack = 0;
288 	if (xxb00t)
289 		mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
290 	mkbootblock(&bootarea, xxboot, bootxx, label, magic);
291 
292 	if (!nowrite) {
293 		off_t	bbo = bbsec * AHDI_BSIZE;
294 		int	fd;
295 
296 		if ((fd = open(devnm, O_WRONLY)) < 0)
297 			err(EXIT_FAILURE, "%s", devnm);
298 		if (lseek(fd, bbo, SEEK_SET) != bbo)
299 			err(EXIT_FAILURE, "%s", devnm);
300 		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
301 			err(EXIT_FAILURE, "%s", devnm);
302 		if (verbose)
303 			printf("Boot block installed on %s (%u)\n", devnm,
304 								    bbsec);
305 		if (xxb00t) {
306 			if (lseek(fd, (off_t)0, SEEK_SET) != 0)
307 				err(EXIT_FAILURE, "%s", devnm);
308 			if (write(fd, &ahdiboot, sizeof(ahdiboot)) != sizeof(ahdiboot))
309 				err(EXIT_FAILURE, "%s", devnm);
310 			if (verbose)
311 				printf("AHDI root  installed on %s (0)\n",
312 								devnm);
313 		}
314 		if (close(fd))
315 			err(EXIT_FAILURE, "%s", devnm);
316 	}
317 }
318 
319 static void
320 install_wd (devnm, label)
321 	char		 *devnm;
322 	struct disklabel *label;
323 {
324 	char		 *xxb00t, *xxboot, *bootxx;
325 	struct disklabel rawlabel;
326 	daddr_t		 bbsec;
327 	u_int		 magic;
328 
329 	if (label->d_partitions[0].p_size == 0)
330 		errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
331 	if (label->d_partitions[0].p_fstype != FS_BSDFFS)
332 		errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
333 		     devnm, fstypenames[label->d_partitions[0].p_fstype]);
334 
335 	bbsec = readdisklabel(devnm, &rawlabel);
336 	if (bbsec == NO_BOOT_BLOCK)
337 		errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
338 	if (memcmp(label, &rawlabel, sizeof(*label)))
339 		errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
340 
341 	if (bbsec) {
342 		xxb00t = alloca(strlen(mdecpath) + 14);
343 		sprintf(xxb00t, "%swdb00t.ahdi", mdecpath);
344 		xxboot = alloca(strlen(mdecpath) + 14);
345 		sprintf(xxboot, "%sxxboot.ahdi", mdecpath);
346 		magic = AHDIMAGIC;
347 	} else {
348 		xxb00t = NULL;
349 		xxboot = alloca(strlen(mdecpath) + 8);
350 		sprintf(xxboot, "%swdboot", mdecpath);
351 		magic = NBDAMAGIC;
352 	}
353 	bootxx = alloca(strlen(mdecpath) + 8);
354 	sprintf(bootxx, "%sbootxx", mdecpath);
355 
356 	if (xxb00t)
357 		mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
358 	mkbootblock(&bootarea, xxboot, bootxx, label, magic);
359 
360 	if (!nowrite) {
361 		int	fd;
362 		off_t	bbo;
363 
364 		bbo = bbsec * AHDI_BSIZE;
365 		if ((fd = open(devnm, O_WRONLY)) < 0)
366 			err(EXIT_FAILURE, "%s", devnm);
367 		if (lseek(fd, bbo, SEEK_SET) != bbo)
368 			err(EXIT_FAILURE, "%s", devnm);
369 		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
370 			err(EXIT_FAILURE, "%s", devnm);
371 		if (verbose)
372 			printf("Boot block installed on %s (%u)\n", devnm,
373 								    bbsec);
374 		if (xxb00t) {
375 			if (lseek(fd, (off_t)0, SEEK_SET) != 0)
376 				err(EXIT_FAILURE, "%s", devnm);
377 			if (write(fd, &ahdiboot, sizeof(ahdiboot))
378 							!= sizeof(ahdiboot))
379 				err(EXIT_FAILURE, "%s", devnm);
380 			if (verbose)
381 				printf("AHDI root  installed on %s (0)\n",
382 									devnm);
383 		}
384 		if (close(fd))
385 			err(EXIT_FAILURE, "%s", devnm);
386 	}
387 }
388 
389 static void
390 mkahdiboot (newroot, xxb00t, devnm, bbsec)
391 	struct ahdi_root *newroot;
392 	char		 *xxb00t,
393 			 *devnm;
394 	daddr_t		 bbsec;
395 {
396 	struct ahdi_root tmproot;
397 	struct ahdi_part *pd;
398 	int		 fd;
399 
400 	/* read prototype root-sector */
401 	if ((fd = open(xxb00t, O_RDONLY)) < 0)
402 		err(EXIT_FAILURE, "%s", xxb00t);
403 	if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot))
404 		err(EXIT_FAILURE, "%s", xxb00t);
405 	if (close(fd))
406 		err(EXIT_FAILURE, "%s", xxb00t);
407 
408 	/* set tracks/cylinder and sectors/track */
409 	setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill));
410 
411 	/* read current root-sector */
412 	if ((fd = open(devnm, O_RDONLY)) < 0)
413 		err(EXIT_FAILURE, "%s", devnm);
414 	if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot))
415 		err(EXIT_FAILURE, "%s", devnm);
416 	if (close(fd))
417 		err(EXIT_FAILURE, "%s", devnm);
418 
419 	/* set bootflags */
420 	for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) {
421 		if (pd->ap_st == bbsec) {
422 			pd->ap_flg = 0x21;	/* bootable, pref = NetBSD */
423 			goto gotit;
424 		}
425 	}
426 	errx(EXIT_FAILURE,
427 	     "%s: NetBSD boot block not on primary AHDI partition.", devnm);
428 
429 gotit:	/* copy code from prototype and set new checksum */
430 	memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill));
431 	newroot->ar_checksum = 0;
432 	newroot->ar_checksum = 0x1234 - abcksum(newroot);
433 
434 	if (verbose)
435 		printf("AHDI      boot loader: %s\n", xxb00t);
436 }
437 
438 static void
439 mkbootblock (bb, xxb, bxx, label, magic)
440 	struct bootblock *bb;
441 	char		 *xxb,
442 			 *bxx;
443 	u_int		 magic;
444 	struct disklabel *label;
445 {
446 	int		 fd;
447 
448 	memset(bb, 0, sizeof(*bb));
449 
450 	/* set boot block magic */
451 	bb->bb_magic = magic;
452 
453 	/* set disk pack label */
454 	BBSETLABEL(bb, label);
455 
456 	/* set second-stage boot loader */
457 	if ((fd = open(bxx, O_RDONLY)) < 0)
458 		err(EXIT_FAILURE, "%s", bxx);
459 	if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx))
460 					!= sizeof(bb->bb_bootxx))
461 		err(EXIT_FAILURE, "%s", bxx);
462 	if (close(fd))
463 		err(EXIT_FAILURE, "%s", bxx);
464 
465 	/* set first-stage bootloader */
466 	if ((fd = open(xxb, O_RDONLY)) < 0)
467 		err(EXIT_FAILURE, "%s", xxb);
468 	if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot))
469 					!= sizeof(bb->bb_xxboot))
470 		err(EXIT_FAILURE, "%s", xxb);
471 	if (close(fd))
472 		err(EXIT_FAILURE, "%s", xxb);
473 
474 	/* set tracks/cylinder and sectors/track */
475 	setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot));
476 
477 	/* set AHDI checksum */
478 	*((u_int16_t *)bb->bb_xxboot + 255) = 0;
479 	*((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot);
480 
481 	if (verbose) {
482 		printf("Primary   boot loader: %s\n", xxb);
483 		printf("Secondary boot loader: %s\n", bxx);
484 	}
485 }
486 
487 static void
488 setIDEpar (start, size)
489 	u_int8_t	*start;
490 	size_t		size;
491 {
492 	static const u_int8_t	mark[] = { 'N', 'e', 't', 'B', 'S', 'D' };
493 
494 	if ((u_int)trackpercyl > 255)
495 		errx(EXIT_FAILURE,
496 		     "%d: Illegal tracks/cylinder value (1..255)", trackpercyl);
497 	if ((u_int)secpertrack > 255)
498 		errx(EXIT_FAILURE,
499 		     "%d: Illegal sectors/track value (1..255)", secpertrack);
500 
501 	if (trackpercyl || secpertrack) {
502 		u_int8_t *p;
503 
504 		if (!trackpercyl)
505 			errx(EXIT_FAILURE, "Need tracks/cylinder too.");
506 		if (!secpertrack)
507 			errx(EXIT_FAILURE, "Need sectors/track too.");
508 
509 		start += 2;
510 		size  -= sizeof(mark) + 2;
511 		for (p = start + size; p >= start; --p) {
512 			if (*p != *mark)
513 				continue;
514 			if (!memcmp(p, mark, sizeof(mark)))
515 				break;
516 		}
517 		if (p < start)
518 			errx(EXIT_FAILURE,
519 			     "Malformatted xxboot prototype.");
520 
521 		*--p = secpertrack;
522 		*--p = trackpercyl;
523 
524 		if (verbose) {
525 			printf("sectors/track  : %d\n", secpertrack);
526 			printf("tracks/cylinder: %d\n", trackpercyl);
527 		}
528 	}
529 }
530 
531 static void
532 setNVpref ()
533 {
534 	static const u_char	bootpref = BOOTPREF_NETBSD;
535 	static const char	nvrdev[] = PATH_NVRAM;
536 
537 	if (!nowrite) {
538 		int	fd;
539 
540 		if ((fd = open(nvrdev, O_RDWR)) < 0)
541 			err(EXIT_FAILURE, "%s", nvrdev);
542 		if (lseek(fd, (off_t)1, SEEK_SET) != 1)
543 			err(EXIT_FAILURE, "%s", nvrdev);
544 		if (write(fd, &bootpref, (size_t)1) != 1)
545 			err(EXIT_FAILURE, "%s", nvrdev);
546 		if (close(fd))
547 			err(EXIT_FAILURE, "%s", nvrdev);
548 		if (verbose)
549 			printf("Boot preference set to NetBSD.\n");
550 	}
551 }
552 
553 static u_int
554 abcksum (bs)
555 	void	*bs;
556 {
557 	u_int16_t sum  = 0,
558 		  *st  = (u_int16_t *)bs,
559 		  *end = (u_int16_t *)bs + 256;
560 
561 	while (st < end)
562 		sum += *st++;
563 	return(sum);
564 }
565