1 /* $NetBSD: installboot.c,v 1.34 2014/11/24 08:08:23 tsutsui 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