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