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