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