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