1 /* $NetBSD: installboot.c,v 1.36 2017/01/11 18:32:48 christos 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