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