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