1/*	$NetBSD: installboot.c,v 1.24 2009/03/14 21:04:07 dsl 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
56static void	usage(void);
57static void	oscheck(void);
58static u_int	abcksum(void *);
59static void	setNVpref(void);
60static void	setIDEpar(u_int8_t *, size_t);
61static void	mkahdiboot(struct ahdi_root *, char *,
62						char *, u_int32_t);
63static void	mkbootblock(struct bootblock *, char *,
64				char *, struct disklabel *, u_int);
65static void	install_fd(char *, struct disklabel *);
66static void	install_sd(char *, struct disklabel *);
67static void	install_wd(char *, struct disklabel *);
68
69static struct bootblock	bootarea;
70static struct ahdi_root ahdiboot;
71static const char	mdecpath[] = PATH_MDEC;
72static const char	stdpath[] = PATH_STD;
73static const char	milanpath[] = PATH_MILAN;
74static int		nowrite = 0;
75static int		verbose = 0;
76static int		trackpercyl = 0;
77static int		secpertrack = 0;
78static int		milan = 0;
79
80static void
81usage(void)
82{
83	fprintf(stderr,
84		"usage: installboot [options] device\n"
85		"where options are:\n"
86		"\t-N  do not actually write anything on the disk\n"
87		"\t-m  use Milan boot blocks\n"
88		"\t-t  number of tracks per cylinder (IDE disk)\n"
89		"\t-u  number of sectors per track (IDE disk)\n"
90		"\t-v  verbose mode\n");
91	exit(EXIT_FAILURE);
92}
93
94int
95main(int argc, char *argv[])
96{
97	struct disklabel dl;
98	char		 *dn;
99	char		 *devchr;
100	int		 fd, c;
101
102	/* check OS bootversion */
103	oscheck();
104
105	/* parse options */
106	while ((c = getopt(argc, argv, "Nmt:u:v")) != -1) {
107		switch (c) {
108		  case 'N':
109			nowrite = 1;
110			break;
111		  case 'm':
112			milan = 1;
113			break;
114		  case 't':
115			trackpercyl = atoi(optarg);
116			break;
117		  case 'u':
118			secpertrack = atoi(optarg);
119			break;
120		  case 'v':
121			verbose = 1;
122			break;
123		  default:
124			usage();
125		}
126	}
127	argv += optind;
128	argc -= optind;
129	if (argc != 1)
130		usage();
131
132	/* get disk label */
133	dn = alloca(sizeof(_PATH_DEV) + strlen(argv[0]) + 8);
134	if (!strchr(argv[0], '/')) {
135		sprintf(dn, "%sr%s%c", _PATH_DEV, argv[0], RAW_PART + 'a');
136		fd = open(dn, O_RDONLY);
137		if (fd < 0 && errno == ENOENT) {
138			sprintf(dn, "%sr%s", _PATH_DEV, argv[0]);
139			fd = open(dn, O_RDONLY);
140		}
141	} else {
142		sprintf(dn, "%s", argv[0]);
143		fd = open(dn, O_RDONLY);
144	}
145	if (fd < 0)
146		err(EXIT_FAILURE, "%s", dn);
147	if (ioctl(fd, DIOCGDINFO, &dl))
148		err(EXIT_FAILURE, "%s: DIOCGDINFO", dn);
149	if (close(fd))
150		err(EXIT_FAILURE, "%s", dn);
151
152	/* Eg: in /dev/fd0c, set devchr to point to the 'f' */
153	devchr = strrchr(dn, '/') + 1;
154	if (*devchr == 'r')
155		++devchr;
156
157	switch (*devchr) {
158		case 'f': /* fd */
159			install_fd(dn, &dl);
160			break;
161		case 'w': /* wd */
162			install_wd(dn, &dl);
163			setNVpref();
164			break;
165		case 's': /* sd */
166			install_sd(dn, &dl);
167			setNVpref();
168			break;
169		default:
170			errx(EXIT_FAILURE,
171			     "%s: '%c': Device type not supported.",
172			     dn, *devchr);
173	}
174
175	return(EXIT_SUCCESS);
176}
177
178static void
179oscheck(void)
180{
181	struct nlist kbv[] = {
182		{ .n_name = "_bootversion" },
183		{ .n_name = NULL }
184	};
185	kvm_t		*kd_kern;
186	char		errbuf[_POSIX2_LINE_MAX];
187	u_short		kvers;
188	struct stat	sb;
189
190	if (stat(_PATH_UNIX, &sb) < 0) {
191		warnx("Cannot stat %s, no bootversion check done", _PATH_UNIX);
192		return;
193	}
194
195	kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
196	if (kd_kern == NULL)
197		errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf);
198	if (kvm_nlist(kd_kern, kbv) == -1)
199		errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd_kern));
200	if (kbv[0].n_value == 0)
201		errx(EXIT_FAILURE, "%s not in namelist", kbv[0].n_name);
202	if (kvm_read(kd_kern, kbv[0].n_value, &kvers, sizeof(kvers)) == -1)
203		errx(EXIT_FAILURE, "kvm_read: %s", kvm_geterr(kd_kern));
204	kvm_close(kd_kern);
205	if (kvers != BOOTVERSION)
206		errx(EXIT_FAILURE, "Kern bootversion: %d, expected: %d",
207		    kvers, BOOTVERSION);
208}
209
210static void
211install_fd(char *devnm, struct disklabel *label)
212{
213	const char	 *machpath;
214	char		 *xxboot, *bootxx;
215	struct partition *rootpart;
216
217	if (label->d_secsize != 512)
218		errx(EXIT_FAILURE,
219		     "%s: %u: Block size not supported.", devnm,
220							  label->d_secsize);
221	if (label->d_ntracks != 2)
222		errx(EXIT_FAILURE,
223		     "%s: Single sided floppy not supported.", devnm);
224
225	if (milan)
226		machpath = milanpath;
227	else
228		machpath = stdpath;
229	xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 8);
230	sprintf(xxboot, "%s%sfdboot", mdecpath, machpath);
231	bootxx = alloca(strlen(mdecpath) + strlen(machpath) + 8);
232	sprintf(bootxx, "%s%sbootxx", mdecpath, machpath);
233
234	/* first used partition (a, b or c) */		/* XXX */
235	for (rootpart = label->d_partitions; ; ++rootpart) {
236		if (rootpart->p_size)
237			break;
238	}
239	if (rootpart != label->d_partitions) {		/* XXX */
240		*(label->d_partitions) = *rootpart;
241		memset(rootpart, 0, sizeof(*rootpart));
242	}
243	label->d_partitions->p_fstype = FS_BSDFFS;	/* XXX */
244	label->d_npartitions = 1;
245	label->d_checksum = 0;
246	label->d_checksum = dkcksum(label);
247
248	trackpercyl = secpertrack = 0;
249	mkbootblock(&bootarea, xxboot, bootxx, label, 0);
250
251	if (!nowrite) {
252		int	fd;
253		if ((fd = open(devnm, O_WRONLY)) < 0)
254			err(EXIT_FAILURE, "%s", devnm);
255		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
256			err(EXIT_FAILURE, "%s", devnm);
257		if (close(fd))
258			err(EXIT_FAILURE, "%s", devnm);
259		if (verbose)
260			printf("Boot block installed on %s\n", devnm);
261	}
262}
263
264static void
265install_sd(char *devnm, struct disklabel *label)
266{
267	const char	 *machpath;
268	char		 *xxb00t, *xxboot, *bootxx;
269	struct disklabel rawlabel;
270	u_int32_t	 bbsec;
271	u_int		 magic;
272
273	if (label->d_partitions[0].p_size == 0)
274		errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
275	if (label->d_partitions[0].p_fstype != FS_BSDFFS)
276		errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
277		     devnm, fstypenames[label->d_partitions[0].p_fstype]);
278
279	bbsec = readdisklabel(devnm, &rawlabel);
280	if (bbsec == NO_BOOT_BLOCK)
281		errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
282	if (memcmp(label, &rawlabel, sizeof(*label)))
283		errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
284
285	if (milan)
286		machpath = milanpath;
287	else
288		machpath = stdpath;
289	if (bbsec) {
290		xxb00t = alloca(strlen(mdecpath) + strlen(machpath) + 14);
291		sprintf(xxb00t, "%s%ssdb00t.ahdi", mdecpath, machpath);
292		xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 14);
293		sprintf(xxboot, "%s%sxxboot.ahdi", mdecpath, machpath);
294		magic = AHDIMAGIC;
295	} else {
296		xxb00t = NULL;
297		xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 8);
298		sprintf(xxboot, "%s%ssdboot", mdecpath, machpath);
299		magic = NBDAMAGIC;
300	}
301	bootxx = alloca(strlen(mdecpath) + strlen(machpath) + 8);
302	sprintf(bootxx, "%s%sbootxx", mdecpath, machpath);
303
304	trackpercyl = secpertrack = 0;
305	if (xxb00t)
306		mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
307	mkbootblock(&bootarea, xxboot, bootxx, label, magic);
308
309	if (!nowrite) {
310		off_t	bbo = (off_t)bbsec * AHDI_BSIZE;
311		int	fd;
312
313		if ((fd = open(devnm, O_WRONLY)) < 0)
314			err(EXIT_FAILURE, "%s", devnm);
315		if (lseek(fd, bbo, SEEK_SET) != bbo)
316			err(EXIT_FAILURE, "%s", devnm);
317		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
318			err(EXIT_FAILURE, "%s", devnm);
319		if (verbose)
320			printf("Boot block installed on %s (sector %d)\n",
321			    devnm, bbsec);
322		if (xxb00t) {
323			if (lseek(fd, (off_t)0, SEEK_SET) != 0)
324				err(EXIT_FAILURE, "%s", devnm);
325			if (write(fd, &ahdiboot, sizeof(ahdiboot)) !=
326			    sizeof(ahdiboot))
327				err(EXIT_FAILURE, "%s", devnm);
328			if (verbose)
329				printf("AHDI root  installed on %s (0)\n",
330				    devnm);
331		}
332		if (close(fd))
333			err(EXIT_FAILURE, "%s", devnm);
334	}
335}
336
337static void
338install_wd(char *devnm, struct disklabel *label)
339{
340	const char	 *machpath;
341	char		 *xxb00t, *xxboot, *bootxx;
342	struct disklabel rawlabel;
343	u_int32_t	 bbsec;
344	u_int		 magic;
345
346	if (label->d_partitions[0].p_size == 0)
347		errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
348	if (label->d_partitions[0].p_fstype != FS_BSDFFS)
349		errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
350		     devnm, fstypenames[label->d_partitions[0].p_fstype]);
351
352	bbsec = readdisklabel(devnm, &rawlabel);
353	if (bbsec == NO_BOOT_BLOCK)
354		errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
355	if (memcmp(label, &rawlabel, sizeof(*label)))
356		errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
357
358	if (milan)
359		machpath = milanpath;
360	else
361		machpath = stdpath;
362	if (bbsec) {
363		xxb00t = alloca(strlen(mdecpath) + strlen(machpath) + 14);
364		sprintf(xxb00t, "%s%swdb00t.ahdi", mdecpath, machpath);
365		xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 14);
366		sprintf(xxboot, "%s%sxxboot.ahdi", mdecpath, machpath);
367		magic = AHDIMAGIC;
368	} else {
369		xxb00t = NULL;
370		xxboot = alloca(strlen(mdecpath) + strlen(machpath) + 8);
371		sprintf(xxboot, "%s%swdboot", mdecpath, machpath);
372		magic = NBDAMAGIC;
373	}
374	bootxx = alloca(strlen(mdecpath) + strlen(machpath) + 8);
375	sprintf(bootxx, "%s%sbootxx", mdecpath, machpath);
376
377	if (xxb00t)
378		mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
379	mkbootblock(&bootarea, xxboot, bootxx, label, magic);
380
381	if (!nowrite) {
382		int	fd;
383		off_t	bbo;
384
385		bbo = (off_t)bbsec * AHDI_BSIZE;
386		if ((fd = open(devnm, O_WRONLY)) < 0)
387			err(EXIT_FAILURE, "%s", devnm);
388		if (lseek(fd, bbo, SEEK_SET) != bbo)
389			err(EXIT_FAILURE, "%s", devnm);
390		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
391			err(EXIT_FAILURE, "%s", devnm);
392		if (verbose)
393			printf("Boot block installed on %s (sector %d)\n",
394			    devnm, bbsec);
395		if (xxb00t) {
396			if (lseek(fd, (off_t)0, SEEK_SET) != 0)
397				err(EXIT_FAILURE, "%s", devnm);
398			if (write(fd, &ahdiboot, sizeof(ahdiboot))
399							!= sizeof(ahdiboot))
400				err(EXIT_FAILURE, "%s", devnm);
401			if (verbose)
402				printf("AHDI root installed on %s (sector 0)\n",
403				    devnm);
404		}
405		if (close(fd))
406			err(EXIT_FAILURE, "%s", devnm);
407	}
408}
409
410static void
411mkahdiboot(struct ahdi_root *newroot, char *xxb00t, char *devnm,
412    u_int32_t bbsec)
413{
414	struct ahdi_root tmproot;
415	struct ahdi_part *pd;
416	int		 fd;
417
418	/* read prototype root-sector */
419	if ((fd = open(xxb00t, O_RDONLY)) < 0)
420		err(EXIT_FAILURE, "%s", xxb00t);
421	if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot))
422		err(EXIT_FAILURE, "%s", xxb00t);
423	if (close(fd))
424		err(EXIT_FAILURE, "%s", xxb00t);
425
426	/* set tracks/cylinder and sectors/track */
427	setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill));
428
429	/* read current root-sector */
430	if ((fd = open(devnm, O_RDONLY)) < 0)
431		err(EXIT_FAILURE, "%s", devnm);
432	if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot))
433		err(EXIT_FAILURE, "%s", devnm);
434	if (close(fd))
435		err(EXIT_FAILURE, "%s", devnm);
436
437	/* set bootflags */
438	for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) {
439		if (pd->ap_st == bbsec) {
440			pd->ap_flg = 0x21;	/* bootable, pref = NetBSD */
441			goto gotit;
442		}
443	}
444	errx(EXIT_FAILURE,
445	     "%s: NetBSD boot block not on primary AHDI partition.", devnm);
446
447gotit:	/* copy code from prototype and set new checksum */
448	memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill));
449	newroot->ar_checksum = 0;
450	newroot->ar_checksum = 0x1234 - abcksum(newroot);
451
452	if (verbose)
453		printf("AHDI      boot loader: %s\n", xxb00t);
454}
455
456static void
457mkbootblock(struct bootblock *bb, char *xxb, char *bxx,
458    struct disklabel *label, u_int magic)
459{
460	int		 fd;
461
462	memset(bb, 0, sizeof(*bb));
463
464	/* set boot block magic */
465	bb->bb_magic = magic;
466
467	/* set disk pack label */
468	BBSETLABEL(bb, label);
469
470	/* set second-stage boot loader */
471	if ((fd = open(bxx, O_RDONLY)) < 0)
472		err(EXIT_FAILURE, "%s", bxx);
473	if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx))
474					!= sizeof(bb->bb_bootxx))
475		err(EXIT_FAILURE, "%s", bxx);
476	if (close(fd))
477		err(EXIT_FAILURE, "%s", bxx);
478
479	/* set first-stage bootloader */
480	if ((fd = open(xxb, O_RDONLY)) < 0)
481		err(EXIT_FAILURE, "%s", xxb);
482	if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot))
483					!= sizeof(bb->bb_xxboot))
484		err(EXIT_FAILURE, "%s", xxb);
485	if (close(fd))
486		err(EXIT_FAILURE, "%s", xxb);
487
488	/* set tracks/cylinder and sectors/track */
489	setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot));
490
491	/* set AHDI checksum */
492	*((u_int16_t *)bb->bb_xxboot + 255) = 0;
493	*((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot);
494
495	if (verbose) {
496		printf("Primary   boot loader: %s\n", xxb);
497		printf("Secondary boot loader: %s\n", bxx);
498	}
499}
500
501static void
502setIDEpar (u_int8_t *start, size_t size)
503{
504	static const u_int8_t	mark[] = { 'N', 'e', 't', 'B', 'S', 'D' };
505
506	if ((u_int)trackpercyl > 255)
507		errx(EXIT_FAILURE,
508		     "%d: Illegal tracks/cylinder value (1..255)", trackpercyl);
509	if ((u_int)secpertrack > 255)
510		errx(EXIT_FAILURE,
511		     "%d: Illegal sectors/track value (1..255)", secpertrack);
512
513	if (trackpercyl || secpertrack) {
514		u_int8_t *p;
515
516		if (!trackpercyl)
517			errx(EXIT_FAILURE, "Need tracks/cylinder too.");
518		if (!secpertrack)
519			errx(EXIT_FAILURE, "Need sectors/track too.");
520
521		start += 2;
522		size  -= sizeof(mark) + 2;
523		for (p = start + size; p >= start; --p) {
524			if (*p != *mark)
525				continue;
526			if (!memcmp(p, mark, sizeof(mark)))
527				break;
528		}
529		if (p < start)
530			errx(EXIT_FAILURE,
531			     "Malformatted xxboot prototype.");
532
533		*--p = secpertrack;
534		*--p = trackpercyl;
535
536		if (verbose) {
537			printf("sectors/track  : %d\n", secpertrack);
538			printf("tracks/cylinder: %d\n", trackpercyl);
539		}
540	}
541}
542
543static void
544setNVpref(void)
545{
546	static const u_char	bootpref = BOOTPREF_NETBSD;
547	static const char	nvrdev[] = PATH_NVRAM;
548
549	if (!nowrite) {
550		int	fd;
551
552		if ((fd = open(nvrdev, O_RDWR)) < 0)
553			err(EXIT_FAILURE, "%s", nvrdev);
554		if (lseek(fd, (off_t)1, SEEK_SET) != 1)
555			err(EXIT_FAILURE, "%s", nvrdev);
556		if (write(fd, &bootpref, (size_t)1) != 1)
557			err(EXIT_FAILURE, "%s", nvrdev);
558		if (close(fd))
559			err(EXIT_FAILURE, "%s", nvrdev);
560		if (verbose)
561			printf("Boot preference set to NetBSD.\n");
562	}
563}
564
565static u_int
566abcksum (void *bs)
567{
568	u_int16_t sum  = 0,
569		  *st  = (u_int16_t *)bs,
570		  *end = (u_int16_t *)bs + 256;
571
572	while (st < end)
573		sum += *st++;
574	return(sum);
575}
576