1 /* $NetBSD: installboot.c,v 1.35 2015/08/12 17:59:51 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 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