xref: /netbsd-src/sys/arch/atari/stand/installboot/installboot.c (revision 220b5c059a84c51ea44107ea8951a57ffaecdc8c)
1 /*	$NetBSD: installboot.c,v 1.12 2001/10/14 19:45:53 leo 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 
177 	kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
178 	if (kd_kern == NULL)
179 		errx(EXIT_FAILURE, "kvm_openfiles: %s", errbuf);
180 	if (kvm_nlist(kd_kern, kbv) == -1)
181 		errx(EXIT_FAILURE, "kvm_nlist: %s", kvm_geterr(kd_kern));
182 	if (kbv[0].n_value == 0)
183 		errx(EXIT_FAILURE, "%s not in namelist", kbv[0].n_name);
184 	if (kvm_read(kd_kern, kbv[0].n_value, (char *)&kvers,
185 						sizeof(kvers)) == -1)
186 		errx(EXIT_FAILURE, "kvm_read: %s", kvm_geterr(kd_kern));
187 	kvm_close(kd_kern);
188 	if (kvers != BOOTVERSION)
189 		errx(EXIT_FAILURE, "Kern bootversion: %d, expected: %d\n",
190 					kvers, BOOTVERSION);
191 }
192 
193 static void
194 install_fd (devnm, label)
195 	char		 *devnm;
196 	struct disklabel *label;
197 {
198 	char		 *xxboot, *bootxx;
199 	struct partition *rootpart;
200 
201 	if (label->d_secsize != 512)
202 		errx(EXIT_FAILURE,
203 		     "%s: %u: Block size not supported.", devnm,
204 							  label->d_secsize);
205 	if (label->d_ntracks != 2)
206 		errx(EXIT_FAILURE,
207 		     "%s: Single sided floppy not supported.", devnm);
208 
209 	xxboot = alloca(strlen(mdecpath) + 8);
210 	sprintf(xxboot, "%sfdboot", mdecpath);
211 	bootxx = alloca(strlen(mdecpath) + 8);
212 	sprintf(bootxx, "%sbootxx", mdecpath);
213 
214 	/* first used partition (a, b or c) */		/* XXX */
215 	for (rootpart = label->d_partitions; ; ++rootpart) {
216 		if (rootpart->p_size)
217 			break;
218 	}
219 	if (rootpart != label->d_partitions) {		/* XXX */
220 		*(label->d_partitions) = *rootpart;
221 		memset(rootpart, 0, sizeof(*rootpart));
222 	}
223 	label->d_partitions->p_fstype = FS_BSDFFS;	/* XXX */
224 	label->d_npartitions = 1;
225 	label->d_checksum = 0;
226 	label->d_checksum = dkcksum(label);
227 
228 	trackpercyl = secpertrack = 0;
229 	mkbootblock(&bootarea, xxboot, bootxx, label, 0);
230 
231 	if (!nowrite) {
232 		int	fd;
233 		if ((fd = open(devnm, O_WRONLY)) < 0)
234 			err(EXIT_FAILURE, "%s", devnm);
235 		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
236 			err(EXIT_FAILURE, "%s", devnm);
237 		if (close(fd))
238 			err(EXIT_FAILURE, "%s", devnm);
239 		if (verbose)
240 			printf("Boot block installed on %s\n", devnm);
241 	}
242 }
243 
244 static void
245 install_sd (devnm, label)
246 	char		 *devnm;
247 	struct disklabel *label;
248 {
249 	char		 *xxb00t, *xxboot, *bootxx;
250 	struct disklabel rawlabel;
251 	daddr_t		 bbsec;
252 	u_int		 magic;
253 
254 	if (label->d_partitions[0].p_size == 0)
255 		errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
256 	if (label->d_partitions[0].p_fstype != FS_BSDFFS)
257 		errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
258 		     devnm, fstypenames[label->d_partitions[0].p_fstype]);
259 
260 	bbsec = readdisklabel(devnm, &rawlabel);
261 	if (bbsec == NO_BOOT_BLOCK)
262 		errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
263 	if (memcmp(label, &rawlabel, sizeof(*label)))
264 		errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
265 
266 	if (bbsec) {
267 		xxb00t = alloca(strlen(mdecpath) + 14);
268 		sprintf(xxb00t, "%ssdb00t.ahdi", mdecpath);
269 		xxboot = alloca(strlen(mdecpath) + 14);
270 		sprintf(xxboot, "%sxxboot.ahdi", mdecpath);
271 		magic = AHDIMAGIC;
272 	} else {
273 		xxb00t = NULL;
274 		xxboot = alloca(strlen(mdecpath) + 8);
275 		sprintf(xxboot, "%ssdboot", mdecpath);
276 		magic = NBDAMAGIC;
277 	}
278 	bootxx = alloca(strlen(mdecpath) + 8);
279 	sprintf(bootxx, "%sbootxx", mdecpath);
280 
281 	trackpercyl = secpertrack = 0;
282 	if (xxb00t)
283 		mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
284 	mkbootblock(&bootarea, xxboot, bootxx, label, magic);
285 
286 	if (!nowrite) {
287 		off_t	bbo = bbsec * AHDI_BSIZE;
288 		int	fd;
289 
290 		if ((fd = open(devnm, O_WRONLY)) < 0)
291 			err(EXIT_FAILURE, "%s", devnm);
292 		if (lseek(fd, bbo, SEEK_SET) != bbo)
293 			err(EXIT_FAILURE, "%s", devnm);
294 		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
295 			err(EXIT_FAILURE, "%s", devnm);
296 		if (verbose)
297 			printf("Boot block installed on %s (%u)\n", devnm,
298 								    bbsec);
299 		if (xxb00t) {
300 			if (lseek(fd, (off_t)0, SEEK_SET) != 0)
301 				err(EXIT_FAILURE, "%s", devnm);
302 			if (write(fd, &ahdiboot, sizeof(ahdiboot)) != sizeof(ahdiboot))
303 				err(EXIT_FAILURE, "%s", devnm);
304 			if (verbose)
305 				printf("AHDI root  installed on %s (0)\n",
306 								devnm);
307 		}
308 		if (close(fd))
309 			err(EXIT_FAILURE, "%s", devnm);
310 	}
311 }
312 
313 static void
314 install_wd (devnm, label)
315 	char		 *devnm;
316 	struct disklabel *label;
317 {
318 	char		 *xxb00t, *xxboot, *bootxx;
319 	struct disklabel rawlabel;
320 	daddr_t		 bbsec;
321 	u_int		 magic;
322 
323 	if (label->d_partitions[0].p_size == 0)
324 		errx(EXIT_FAILURE, "%s: No root-filesystem.", devnm);
325 	if (label->d_partitions[0].p_fstype != FS_BSDFFS)
326 		errx(EXIT_FAILURE, "%s: %s: Illegal root-filesystem type.",
327 		     devnm, fstypenames[label->d_partitions[0].p_fstype]);
328 
329 	bbsec = readdisklabel(devnm, &rawlabel);
330 	if (bbsec == NO_BOOT_BLOCK)
331 		errx(EXIT_FAILURE, "%s: No NetBSD boot block.", devnm);
332 	if (memcmp(label, &rawlabel, sizeof(*label)))
333 		errx(EXIT_FAILURE, "%s: Invalid NetBSD boot block.", devnm);
334 
335 	if (bbsec) {
336 		xxb00t = alloca(strlen(mdecpath) + 14);
337 		sprintf(xxb00t, "%swdb00t.ahdi", mdecpath);
338 		xxboot = alloca(strlen(mdecpath) + 14);
339 		sprintf(xxboot, "%sxxboot.ahdi", mdecpath);
340 		magic = AHDIMAGIC;
341 	} else {
342 		xxb00t = NULL;
343 		xxboot = alloca(strlen(mdecpath) + 8);
344 		sprintf(xxboot, "%swdboot", mdecpath);
345 		magic = NBDAMAGIC;
346 	}
347 	bootxx = alloca(strlen(mdecpath) + 8);
348 	sprintf(bootxx, "%sbootxx", mdecpath);
349 
350 	if (xxb00t)
351 		mkahdiboot(&ahdiboot, xxb00t, devnm, bbsec);
352 	mkbootblock(&bootarea, xxboot, bootxx, label, magic);
353 
354 	if (!nowrite) {
355 		int	fd;
356 		off_t	bbo;
357 
358 		bbo = bbsec * AHDI_BSIZE;
359 		if ((fd = open(devnm, O_WRONLY)) < 0)
360 			err(EXIT_FAILURE, "%s", devnm);
361 		if (lseek(fd, bbo, SEEK_SET) != bbo)
362 			err(EXIT_FAILURE, "%s", devnm);
363 		if (write(fd, &bootarea, sizeof(bootarea)) != sizeof(bootarea))
364 			err(EXIT_FAILURE, "%s", devnm);
365 		if (verbose)
366 			printf("Boot block installed on %s (%u)\n", devnm,
367 								    bbsec);
368 		if (xxb00t) {
369 			if (lseek(fd, (off_t)0, SEEK_SET) != 0)
370 				err(EXIT_FAILURE, "%s", devnm);
371 			if (write(fd, &ahdiboot, sizeof(ahdiboot))
372 							!= sizeof(ahdiboot))
373 				err(EXIT_FAILURE, "%s", devnm);
374 			if (verbose)
375 				printf("AHDI root  installed on %s (0)\n",
376 									devnm);
377 		}
378 		if (close(fd))
379 			err(EXIT_FAILURE, "%s", devnm);
380 	}
381 }
382 
383 static void
384 mkahdiboot (newroot, xxb00t, devnm, bbsec)
385 	struct ahdi_root *newroot;
386 	char		 *xxb00t,
387 			 *devnm;
388 	daddr_t		 bbsec;
389 {
390 	struct ahdi_root tmproot;
391 	struct ahdi_part *pd;
392 	int		 fd;
393 
394 	/* read prototype root-sector */
395 	if ((fd = open(xxb00t, O_RDONLY)) < 0)
396 		err(EXIT_FAILURE, "%s", xxb00t);
397 	if (read(fd, &tmproot, sizeof(tmproot)) != sizeof(tmproot))
398 		err(EXIT_FAILURE, "%s", xxb00t);
399 	if (close(fd))
400 		err(EXIT_FAILURE, "%s", xxb00t);
401 
402 	/* set tracks/cylinder and sectors/track */
403 	setIDEpar(tmproot.ar_fill, sizeof(tmproot.ar_fill));
404 
405 	/* read current root-sector */
406 	if ((fd = open(devnm, O_RDONLY)) < 0)
407 		err(EXIT_FAILURE, "%s", devnm);
408 	if (read(fd, newroot, sizeof(*newroot)) != sizeof(*newroot))
409 		err(EXIT_FAILURE, "%s", devnm);
410 	if (close(fd))
411 		err(EXIT_FAILURE, "%s", devnm);
412 
413 	/* set bootflags */
414 	for (pd = newroot->ar_parts; pd-newroot->ar_parts < AHDI_MAXRPD; ++pd) {
415 		if (pd->ap_st == bbsec) {
416 			pd->ap_flg = 0x21;	/* bootable, pref = NetBSD */
417 			goto gotit;
418 		}
419 	}
420 	errx(EXIT_FAILURE,
421 	     "%s: NetBSD boot block not on primary AHDI partition.", devnm);
422 
423 gotit:	/* copy code from prototype and set new checksum */
424 	memcpy(newroot->ar_fill, tmproot.ar_fill, sizeof(tmproot.ar_fill));
425 	newroot->ar_checksum = 0;
426 	newroot->ar_checksum = 0x1234 - abcksum(newroot);
427 
428 	if (verbose)
429 		printf("AHDI      boot loader: %s\n", xxb00t);
430 }
431 
432 static void
433 mkbootblock (bb, xxb, bxx, label, magic)
434 	struct bootblock *bb;
435 	char		 *xxb,
436 			 *bxx;
437 	u_int		 magic;
438 	struct disklabel *label;
439 {
440 	int		 fd;
441 
442 	memset(bb, 0, sizeof(*bb));
443 
444 	/* set boot block magic */
445 	bb->bb_magic = magic;
446 
447 	/* set disk pack label */
448 	BBSETLABEL(bb, label);
449 
450 	/* set second-stage boot loader */
451 	if ((fd = open(bxx, O_RDONLY)) < 0)
452 		err(EXIT_FAILURE, "%s", bxx);
453 	if (read(fd, bb->bb_bootxx, sizeof(bb->bb_bootxx))
454 					!= sizeof(bb->bb_bootxx))
455 		err(EXIT_FAILURE, "%s", bxx);
456 	if (close(fd))
457 		err(EXIT_FAILURE, "%s", bxx);
458 
459 	/* set first-stage bootloader */
460 	if ((fd = open(xxb, O_RDONLY)) < 0)
461 		err(EXIT_FAILURE, "%s", xxb);
462 	if (read(fd, bb->bb_xxboot, sizeof(bb->bb_xxboot))
463 					!= sizeof(bb->bb_xxboot))
464 		err(EXIT_FAILURE, "%s", xxb);
465 	if (close(fd))
466 		err(EXIT_FAILURE, "%s", xxb);
467 
468 	/* set tracks/cylinder and sectors/track */
469 	setIDEpar(bb->bb_xxboot, sizeof(bb->bb_xxboot));
470 
471 	/* set AHDI checksum */
472 	*((u_int16_t *)bb->bb_xxboot + 255) = 0;
473 	*((u_int16_t *)bb->bb_xxboot + 255) = 0x1234 - abcksum(bb->bb_xxboot);
474 
475 	if (verbose) {
476 		printf("Primary   boot loader: %s\n", xxb);
477 		printf("Secondary boot loader: %s\n", bxx);
478 	}
479 }
480 
481 static void
482 setIDEpar (start, size)
483 	u_int8_t	*start;
484 	size_t		size;
485 {
486 	static const u_int8_t	mark[] = { 'N', 'e', 't', 'B', 'S', 'D' };
487 
488 	if ((u_int)trackpercyl > 255)
489 		errx(EXIT_FAILURE,
490 		     "%d: Illegal tracks/cylinder value (1..255)", trackpercyl);
491 	if ((u_int)secpertrack > 255)
492 		errx(EXIT_FAILURE,
493 		     "%d: Illegal sectors/track value (1..255)", secpertrack);
494 
495 	if (trackpercyl || secpertrack) {
496 		u_int8_t *p;
497 
498 		if (!trackpercyl)
499 			errx(EXIT_FAILURE, "Need tracks/cylinder too.");
500 		if (!secpertrack)
501 			errx(EXIT_FAILURE, "Need sectors/track too.");
502 
503 		start += 2;
504 		size  -= sizeof(mark) + 2;
505 		for (p = start + size; p >= start; --p) {
506 			if (*p != *mark)
507 				continue;
508 			if (!memcmp(p, mark, sizeof(mark)))
509 				break;
510 		}
511 		if (p < start)
512 			errx(EXIT_FAILURE,
513 			     "Malformatted xxboot prototype.");
514 
515 		*--p = secpertrack;
516 		*--p = trackpercyl;
517 
518 		if (verbose) {
519 			printf("sectors/track  : %d\n", secpertrack);
520 			printf("tracks/cylinder: %d\n", trackpercyl);
521 		}
522 	}
523 }
524 
525 static void
526 setNVpref ()
527 {
528 	static const u_char	bootpref = BOOTPREF_NETBSD;
529 	static const char	nvrdev[] = PATH_NVRAM;
530 
531 	if (!nowrite) {
532 		int	fd;
533 
534 		if ((fd = open(nvrdev, O_RDWR)) < 0)
535 			err(EXIT_FAILURE, "%s", nvrdev);
536 		if (lseek(fd, (off_t)1, SEEK_SET) != 1)
537 			err(EXIT_FAILURE, "%s", nvrdev);
538 		if (write(fd, &bootpref, (size_t)1) != 1)
539 			err(EXIT_FAILURE, "%s", nvrdev);
540 		if (close(fd))
541 			err(EXIT_FAILURE, "%s", nvrdev);
542 		if (verbose)
543 			printf("Boot preference set to NetBSD.\n");
544 	}
545 }
546 
547 static u_int
548 abcksum (bs)
549 	void	*bs;
550 {
551 	u_int16_t sum  = 0,
552 		  *st  = (u_int16_t *)bs,
553 		  *end = (u_int16_t *)bs + 256;
554 
555 	while (st < end)
556 		sum += *st++;
557 	return(sum);
558 }
559