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