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