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